Merge branch 'dev_group_chat'

This commit is contained in:
Benjamin Reis 2018-08-03 13:14:59 +02:00
commit f65f0af7c0
55 changed files with 3121 additions and 383 deletions

3
.gitmodules vendored
View file

@ -117,3 +117,6 @@
[submodule "submodules/externals/soci"]
path = submodules/externals/soci
url = https://gitlab.linphone.org/BC/public/external/soci.git
[submodule "submodules/externals/zxing-cpp"]
path = submodules/externals/zxing-cpp
url = https://gitlab.linphone.org/BC/public/external/zxing-cpp.git

View file

@ -21,6 +21,7 @@
#import "UICompositeView.h"
#import "TPKeyboardAvoidingScrollView.h"
#import "PhoneMainView.h"
#import <AVFoundation/AVFoundation.h>
@interface AssistantView : UIViewController <UITextFieldDelegate, UICompositeViewDelegate> {
@ -51,6 +52,7 @@
@property(nonatomic, strong) IBOutlet UIView *remoteProvisioningLoginView;
@property(strong, nonatomic) IBOutlet UIView *remoteProvisioningView;
@property (strong, nonatomic) IBOutlet UIView *createAccountActivateSMSView;
@property (strong, nonatomic) IBOutlet UIView *qrCodeView;
@property(nonatomic, strong) IBOutlet UIImageView *welcomeLogoImage;
@property(nonatomic, strong) IBOutlet UIButton *gotoCreateAccountButton;
@ -64,6 +66,9 @@
@property (weak, nonatomic) IBOutlet UILabel *activationSMSText;
@property (weak, nonatomic) IBOutlet UILabel *accountLabel;
@property (weak, nonatomic) IBOutlet UIButton *qrCodeButton;
@property (weak, nonatomic) IBOutlet UIButton *downloadButton;
@property (weak, nonatomic) IBOutlet UITextField *urlLabel;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *createAccountNextButtonPositionConstraint;
+ (NSString *)StringForXMLRPCError:(const char *)err;
@ -92,6 +97,7 @@
- (IBAction)onLoginClick:(id)sender;
- (IBAction)onRemoteProvisioningLoginClick:(id)sender;
- (IBAction)onRemoteProvisioningDownloadClick:(id)sender;
- (IBAction)onLaunchQRCodeView:(id)sender;
- (IBAction)onCreateAccountCheckActivatedClick:(id)sender;
- (IBAction)onLinkAccountClick:(id)sender;

View file

@ -28,6 +28,7 @@
#import "PhoneMainView.h"
#import "UIAssistantTextField.h"
#import "UITextField+DoneButton.h"
#import "LinphoneAppDelegate.h"
typedef enum _ViewElement {
ViewElement_Username = 100,
@ -117,6 +118,7 @@ static UICompositeViewDescription *compositeDescription = nil;
}
mustRestoreView = NO;
_outgoingView = DialerView.compositeViewDescription;
_qrCodeButton.hidden = !ENABLE_QRCODE;
[self resetLiblinphone:FALSE];
}
@ -134,6 +136,8 @@ static UICompositeViewDescription *compositeDescription = nil;
[_contentView setContentSize:frame.size];
[_contentView contentSizeToFit];
_qrCodeView.frame = [[UIScreen mainScreen] bounds];
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
@ -474,6 +478,13 @@ static UICompositeViewDescription *compositeDescription = nil;
view = _linphoneLoginView;
}
}
if (currentView == _qrCodeView) {
linphone_core_enable_video_preview(LC, FALSE);
linphone_core_enable_qrcode_video_preview(LC, FALSE);
LinphoneAppDelegate *delegate = (LinphoneAppDelegate *)UIApplication.sharedApplication.delegate;
delegate.onlyPortrait = FALSE;
}
// Animation
if (animation && ANIMATED) {
@ -1246,9 +1257,9 @@ void assistant_is_account_linked(LinphoneAccountCreator *creator, LinphoneAccoun
- (IBAction)onGotoRemoteProvisioningClick:(id)sender {
ONCLICKBUTTON(sender, 100, {
nextView = _remoteProvisioningView;
[self loadAssistantConfig:@"assistant_remote.rc"];
[self findTextField:ViewElement_URL].text =
[LinphoneManager.instance lpConfigStringForKey:@"config-uri" inSection:@"misc"];
[self loadAssistantConfig:@"assistant_remote.rc"];
});
}
@ -1396,6 +1407,49 @@ void assistant_is_account_linked(LinphoneAccountCreator *creator, LinphoneAccoun
});
}
- (IBAction)onLaunchQRCodeView:(id)sender {
[NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(qrCodeFound:)
name:kLinphoneQRCodeFound
object:nil];
LinphoneAppDelegate *delegate = (LinphoneAppDelegate *)UIApplication.sharedApplication.delegate;
delegate.onlyPortrait = TRUE;
NSNumber *value = [NSNumber numberWithInt:UIDeviceOrientationPortrait];
[[UIDevice currentDevice] setValue:value forKey:@"orientation"];
//[UIViewController attemptRotationToDeviceOrientation];
AVCaptureDevice *backCamera = [AVCaptureDevice defaultDeviceWithDeviceType:AVCaptureDeviceTypeBuiltInWideAngleCamera mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionBack];
if (![[NSString stringWithUTF8String:linphone_core_get_video_device(LC)] containsString:[backCamera uniqueID]]) {
bctbx_list_t *deviceList = linphone_core_get_video_devices_list(LC);
NSMutableArray *devices = [NSMutableArray array];
while (deviceList) {
char *data = deviceList->data;
[devices addObject:[NSString stringWithUTF8String:data]];
deviceList = deviceList->next;
}
bctbx_list_free(deviceList);
for (NSString *device in devices) {
if ([device containsString:backCamera.uniqueID]) {
linphone_core_set_video_device(LC, device.UTF8String);
}
}
}
linphone_core_set_native_preview_window_id(LC, (__bridge void *)(_qrCodeView));
linphone_core_enable_video_preview(LC, TRUE);
linphone_core_enable_qrcode_video_preview(LC, TRUE);
[NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(qrCodeFound:)
name:kLinphoneQRCodeFound
object:nil];
[self changeView:_qrCodeView back:FALSE animation:TRUE];
}
- (void)refreshYourUsername {
UIAssistantTextField *username = [self findTextField:ViewElement_Username];
UIAssistantTextField *phone = [self findTextField:ViewElement_Phone];
@ -1509,7 +1563,7 @@ void assistant_is_account_linked(LinphoneAccountCreator *creator, LinphoneAccoun
- (IBAction)onBackClick:(id)sender {
if ([historyViews count] > 0) {
if (currentView == _createAccountActivateSMSView || currentView == _createAccountActivateEmailView) {
if (currentView == _createAccountActivateSMSView || currentView == _createAccountActivateEmailView || currentView == _qrCodeView) {
UIView *view = [historyViews lastObject];
[historyViews removeLastObject];
[self changeView:view back:TRUE animation:TRUE];
@ -1532,7 +1586,7 @@ void assistant_is_account_linked(LinphoneAccountCreator *creator, LinphoneAccoun
#pragma mark - select country delegate
- (void)didSelectCountry:(NSDictionary *)country{
- (void)didSelectCountry:(NSDictionary *)country {
UIRoundBorderedButton *phoneButton = [self findButton:ViewElement_PhoneButton];
[phoneButton setTitle:[country objectForKey:@"name"] forState:UIControlStateNormal];
UIAssistantTextField* countryCodeField = [self findTextField:ViewElement_PhoneCC];
@ -1541,4 +1595,23 @@ void assistant_is_account_linked(LinphoneAccountCreator *creator, LinphoneAccoun
[self shouldEnableNextButton];
}
-(void)qrCodeFound:(NSNotification *)notif {
if ([notif.userInfo count] == 0){
return;
}
[NSNotificationCenter.defaultCenter removeObserver:self name:kLinphoneQRCodeFound object:nil];
dispatch_async(dispatch_get_main_queue(), ^{
self.urlLabel.text = [notif.userInfo objectForKey:@"qrcode"];
});
if ([historyViews count] > 0) {
if (currentView == _qrCodeView) {
UIView *view = [historyViews lastObject];
[historyViews removeLastObject];
[self changeView:view back:TRUE animation:TRUE];
} else {
[self changeView:_welcomeView back:TRUE animation:TRUE];
}
}
}
@end

View file

@ -1,13 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11542" systemVersion="16B2555" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" 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="11524"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
<capability name="Alignment constraints to the first baseline" minToolsVersion="6.0"/>
<capability name="Alignment constraints with different attributes" minToolsVersion="5.1"/>
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@ -22,6 +21,7 @@
<outlet property="createAccountActivateEmailView" destination="Zd9-Ms-s7X" id="6Qs-np-oqS"/>
<outlet property="createAccountActivateSMSView" destination="101" id="SAS-8i-4Ao"/>
<outlet property="createAccountView" destination="44" id="70"/>
<outlet property="downloadButton" destination="eM0-bn-v3C" id="hsP-3U-clZ"/>
<outlet property="gotoCreateAccountButton" destination="36" id="bZf-lI-yJp"/>
<outlet property="gotoLinphoneLoginButton" destination="38" id="K1E-r5-WiL"/>
<outlet property="gotoLoginButton" destination="39" id="uSY-cr-j4w"/>
@ -31,9 +31,12 @@
<outlet property="loginView" destination="56" id="bJH-N8-uPi"/>
<outlet property="phoneLabel" destination="ZSJ-Lv-n60" id="5Qi-xR-wyK"/>
<outlet property="phoneTitle" destination="rAy-0V-sqv" id="MZq-EF-6HF"/>
<outlet property="qrCodeButton" destination="2T9-bt-FHD" id="5M5-Fj-q4E"/>
<outlet property="qrCodeView" destination="Z2R-SD-yFJ" id="dcs-rK-HgK"/>
<outlet property="remoteProvisioningLoginView" destination="xVK-hL-6pe" id="pKL-9M-Vsa"/>
<outlet property="remoteProvisioningView" destination="Zuh-Sd-pcd" id="3pa-2M-5Xq"/>
<outlet property="subtileLabel_useLinphoneAccount" destination="4n3-ZD-KVi" id="8d9-0f-2Vi"/>
<outlet property="urlLabel" destination="Ffg-Of-xyh" id="IhC-hf-1OG"/>
<outlet property="welcomeView" destination="33" id="46a-AR-5mN"/>
</connections>
</placeholder>
@ -338,7 +341,7 @@
<rect key="frame" x="0.0" y="426" width="375" height="237"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="EMAIL" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="E7B-Ag-ltX" userLabel="emailLabel">
<rect key="frame" x="38" y="0.0" width="299" height="14"/>
<rect key="frame" x="38" y="0.0" width="291" height="14"/>
<constraints>
<constraint firstAttribute="height" constant="14" id="1pi-x8-d7G"/>
</constraints>
@ -347,7 +350,7 @@
<nil key="highlightedColor"/>
</label>
<textField opaque="NO" clipsSubviews="YES" tag="103" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" adjustsFontSizeToFit="NO" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="IXr-i2-LEh" userLabel="emailField" customClass="UIAssistantTextField">
<rect key="frame" x="38" y="22" width="299" height="30"/>
<rect key="frame" x="38" y="22" width="291" height="30"/>
<color key="backgroundColor" red="0.85415387153625488" green="0.85412830114364624" blue="0.85414278507232666" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" label="Email"/>
<color key="textColor" red="0.20521381497383118" green="0.20520767569541931" blue="0.20521116256713867" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@ -359,7 +362,7 @@
</connections>
</textField>
<label opaque="NO" userInteractionEnabled="NO" tag="410" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Invalid email" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="1r3-gy-l29" userLabel="emailErrorLabel">
<rect key="frame" x="38" y="52" width="299" height="10"/>
<rect key="frame" x="38" y="52" width="291" height="10"/>
<constraints>
<constraint firstAttribute="height" constant="10" id="DxN-0P-gw6"/>
</constraints>
@ -368,7 +371,7 @@
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="PASSWORD" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="p25-jF-I9X" userLabel="passwordLabel">
<rect key="frame" x="38" y="76" width="299" height="14"/>
<rect key="frame" x="38" y="76" width="291" height="14"/>
<constraints>
<constraint firstAttribute="height" constant="14" id="yWO-rf-S2R"/>
</constraints>
@ -377,7 +380,7 @@
<nil key="highlightedColor"/>
</label>
<textField opaque="NO" clipsSubviews="YES" tag="101" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" adjustsFontSizeToFit="NO" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="t14-fe-grq" userLabel="passwordField" customClass="UIAssistantTextField">
<rect key="frame" x="38" y="101" width="299" height="30"/>
<rect key="frame" x="38" y="101" width="291" height="30"/>
<color key="backgroundColor" red="0.85415387153625488" green="0.85412830114364624" blue="0.85414278507232666" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" label="Password "/>
<color key="textColor" red="0.20521381497383118" green="0.20520767569541931" blue="0.20521116256713867" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@ -389,7 +392,7 @@
</connections>
</textField>
<label opaque="NO" userInteractionEnabled="NO" tag="410" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Invalid password" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="J5q-GY-AW4" userLabel="passwordErrorLabel">
<rect key="frame" x="38" y="131" width="299" height="10"/>
<rect key="frame" x="38" y="131" width="291" height="10"/>
<constraints>
<constraint firstAttribute="height" constant="10" id="EZT-D6-c3H"/>
</constraints>
@ -398,7 +401,7 @@
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="PASSWORD CONFIRMATION" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fpk-Vv-LB0" userLabel="password2Label">
<rect key="frame" x="38" y="153" width="299" height="14"/>
<rect key="frame" x="38" y="153" width="291" height="14"/>
<constraints>
<constraint firstAttribute="height" constant="14" id="aRv-BL-3ug"/>
</constraints>
@ -407,7 +410,7 @@
<nil key="highlightedColor"/>
</label>
<textField opaque="NO" clipsSubviews="YES" tag="102" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" adjustsFontSizeToFit="NO" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="kHS-3H-oHM" userLabel="password2Field" customClass="UIAssistantTextField">
<rect key="frame" x="38" y="175" width="299" height="30"/>
<rect key="frame" x="38" y="175" width="291" height="30"/>
<color key="backgroundColor" red="0.85415387153625488" green="0.85412830114364624" blue="0.85414278507232666" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" label="Password confirmation"/>
<color key="textColor" red="0.20521381497383118" green="0.20520767569541931" blue="0.20521116256713867" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@ -418,7 +421,7 @@
</connections>
</textField>
<label opaque="NO" userInteractionEnabled="NO" tag="410" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Passwords do not match" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="DgP-rE-2dp" userLabel="password2ErrorLabel">
<rect key="frame" x="38" y="205" width="299" height="10"/>
<rect key="frame" x="38" y="205" width="291" height="10"/>
<constraints>
<constraint firstAttribute="height" constant="10" id="gT0-em-DfO"/>
</constraints>
@ -1273,11 +1276,11 @@ Once it is done, come back here and click on the button.</string>
<point key="canvasLocation" x="1490" y="264"/>
</view>
<view contentMode="scaleToFill" id="Zuh-Sd-pcd" userLabel="remoteProvisioningView">
<rect key="frame" x="0.0" y="0.0" width="375" height="241"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="347"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="FETCH REMOTE CONFIGURATION" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="dpv-8C-qt6" userLabel="titleLabel">
<rect key="frame" x="36" y="0.0" width="303" height="59"/>
<rect key="frame" x="36" y="0.0" width="303" height="29"/>
<accessibility key="accessibilityConfiguration" label="Account setup assistant"/>
<fontDescription key="fontDescription" type="system" weight="light" pointSize="24"/>
<color key="textColor" red="0.20521381497383118" green="0.20520767569541931" blue="0.20521116256713867" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@ -1285,7 +1288,7 @@ Once it is done, come back here and click on the button.</string>
<size key="shadowOffset" width="-1" height="-1"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Please provide your provisioning URL." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="hEy-Xe-afq" userLabel="subtitleLabel">
<rect key="frame" x="36" y="66" width="303" height="29"/>
<rect key="frame" x="36" y="36" width="303" height="29"/>
<accessibility key="accessibilityConfiguration" label="Account setup assistant"/>
<constraints>
<constraint firstAttribute="height" constant="29" id="vbU-BF-C6f"/>
@ -1296,7 +1299,7 @@ Once it is done, come back here and click on the button.</string>
<size key="shadowOffset" width="-1" height="-1"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="URL" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="0tr-gN-2Ce" userLabel="urlLabel">
<rect key="frame" x="38" y="131" width="299" height="14"/>
<rect key="frame" x="38" y="101" width="299" height="14"/>
<constraints>
<constraint firstAttribute="height" constant="14" id="1xT-bm-Ag5"/>
</constraints>
@ -1305,7 +1308,7 @@ Once it is done, come back here and click on the button.</string>
<nil key="highlightedColor"/>
</label>
<textField opaque="NO" clipsSubviews="YES" tag="105" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" adjustsFontSizeToFit="NO" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="Ffg-Of-xyh" userLabel="urlField" customClass="UIAssistantTextField">
<rect key="frame" x="38" y="153" width="299" height="30"/>
<rect key="frame" x="38" y="123" width="299" height="30"/>
<color key="backgroundColor" red="0.85415387153625488" green="0.85412830114364624" blue="0.85414278507232666" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" label="URL"/>
<constraints>
@ -1320,7 +1323,7 @@ Once it is done, come back here and click on the button.</string>
</connections>
</textField>
<label opaque="NO" userInteractionEnabled="NO" tag="410" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Invalid URL" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="uEq-Gk-9iy" userLabel="urlErrorLabel">
<rect key="frame" x="38" y="183" width="299" height="10"/>
<rect key="frame" x="38" y="153" width="299" height="10"/>
<constraints>
<constraint firstAttribute="height" constant="10" id="VcL-Iz-4n4"/>
</constraints>
@ -1328,8 +1331,8 @@ Once it is done, come back here and click on the button.</string>
<color key="textColor" red="0.98594832420349121" green="0.0" blue="0.026950567960739136" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" tag="130" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="eM0-bn-v3C" userLabel="downloadButton" customClass="UIRoundBorderedButton">
<rect key="frame" x="38" y="201" width="299" height="40"/>
<button opaque="NO" tag="130" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="eM0-bn-v3C" userLabel="downloadButton" customClass="UIRoundBorderedButton">
<rect key="frame" x="38" y="171" width="299" height="40"/>
<accessibility key="accessibilityConfiguration" label="Fetch and apply">
<bool key="isElement" value="YES"/>
</accessibility>
@ -1348,6 +1351,26 @@ Once it is done, come back here and click on the button.</string>
<action selector="onRemoteProvisioningDownloadClick:" destination="-1" eventType="touchUpInside" id="6yF-1w-Hpa"/>
</connections>
</button>
<button hidden="YES" opaque="NO" tag="130" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="2T9-bt-FHD" userLabel="qrcodeButton" customClass="UIRoundBorderedButton">
<rect key="frame" x="38" y="219" width="299" height="40"/>
<accessibility key="accessibilityConfiguration" label="Fetch and apply">
<bool key="isElement" value="YES"/>
</accessibility>
<constraints>
<constraint firstAttribute="height" constant="40" id="Wcc-Sn-c0r"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<state key="normal" title="QR CODE">
<color key="titleColor" red="0.20521381499999999" green="0.20520767570000001" blue="0.2052111626" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<state key="disabled">
<color key="titleColor" red="0.71885228160000003" green="0.71883076430000004" blue="0.71884298319999995" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<state key="highlighted" backgroundImage="color_F.png"/>
<connections>
<action selector="onLaunchQRCodeView:" destination="-1" eventType="touchUpInside" id="i1k-Su-mAZ"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
@ -1366,14 +1389,16 @@ Once it is done, come back here and click on the button.</string>
<constraint firstItem="0tr-gN-2Ce" firstAttribute="trailing" secondItem="Ffg-Of-xyh" secondAttribute="trailing" id="d5L-lU-eEr"/>
<constraint firstItem="hEy-Xe-afq" firstAttribute="centerX" secondItem="0tr-gN-2Ce" secondAttribute="centerX" id="dQg-gY-T2t"/>
<constraint firstItem="Ffg-Of-xyh" firstAttribute="top" secondItem="0tr-gN-2Ce" secondAttribute="bottom" constant="8" symbolic="YES" id="deZ-RJ-Tac"/>
<constraint firstItem="2T9-bt-FHD" firstAttribute="top" secondItem="eM0-bn-v3C" secondAttribute="bottom" constant="8" id="hPc-df-UEy"/>
<constraint firstItem="hEy-Xe-afq" firstAttribute="top" secondItem="dpv-8C-qt6" secondAttribute="bottom" constant="7" id="j4R-8z-ku5"/>
<constraint firstItem="dpv-8C-qt6" firstAttribute="leading" secondItem="hEy-Xe-afq" secondAttribute="leading" id="jKd-Ac-gLa"/>
<constraint firstAttribute="bottom" secondItem="eM0-bn-v3C" secondAttribute="bottom" id="kCn-do-O9N"/>
<constraint firstItem="uEq-Gk-9iy" firstAttribute="trailing" secondItem="eM0-bn-v3C" secondAttribute="trailing" id="kFa-lR-RH4"/>
<constraint firstItem="2T9-bt-FHD" firstAttribute="leading" secondItem="eM0-bn-v3C" secondAttribute="leading" id="kP2-1p-Fpw"/>
<constraint firstItem="2T9-bt-FHD" firstAttribute="trailing" secondItem="eM0-bn-v3C" secondAttribute="trailing" id="vXZ-nV-P1E"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="1917.5" y="96.5"/>
<point key="canvasLocation" x="1917.5" y="149.5"/>
</view>
<view contentMode="scaleToFill" id="xVK-hL-6pe" userLabel="remoteProvisioningLoginView">
<rect key="frame" x="0.0" y="0.0" width="375" height="379"/>
@ -1524,13 +1549,19 @@ Once it is done, come back here and click on the button.</string>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="1917.5" y="457.5"/>
<point key="canvasLocation" x="2422" y="410"/>
</view>
<tapGestureRecognizer id="VEO-W5-GDl" userLabel="onLinkTap">
<connections>
<action selector="onLinkTap:" destination="-1" id="xXJ-65-gxy"/>
</connections>
</tapGestureRecognizer>
<view contentMode="scaleToFill" id="Z2R-SD-yFJ" userLabel="qrCodeView">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<point key="canvasLocation" x="1918" y="554"/>
</view>
</objects>
<resources>
<image name="color_F.png" width="2" height="2"/>

View file

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13196" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" colorMatched="YES">
<device id="retina4_7" orientation="landscape">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13174"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@ -35,23 +35,23 @@
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="v2I-ka-LYa" userLabel="iphone6MetricsView">
<rect key="frame" x="0.0" y="0.0" width="667" height="375"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<view tag="1" contentMode="scaleToFill" id="6">
<rect key="frame" x="0.0" y="42" width="667" height="267"/>
<rect key="frame" x="0.0" y="42" width="375" height="559"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view tag="2" contentMode="scaleToFill" id="7" userLabel="topBar">
<rect key="frame" x="0.0" y="0.0" width="667" height="66"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" tag="3" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="color_F.png" id="v5G-Qf-X82" userLabel="backgroundColor">
<rect key="frame" x="0.0" y="0.0" width="667" height="66"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
</imageView>
<button opaque="NO" tag="4" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" id="9" userLabel="backButton" customClass="UIIconButton">
<rect key="frame" x="0.0" y="0.0" width="133" height="66"/>
<rect key="frame" x="0.0" y="0.0" width="75" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
<accessibility key="accessibilityConfiguration" label="Back"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
@ -66,7 +66,7 @@
</connections>
</button>
<button hidden="YES" opaque="NO" tag="11" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="bci-3K-AcG" userLabel="cancelButton" customClass="UIIconButton">
<rect key="frame" x="0.0" y="0.0" width="133" height="66"/>
<rect key="frame" x="0.0" y="0.0" width="75" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
<accessibility key="accessibilityConfiguration" label="Cancel"/>
<inset key="titleEdgeInsets" minX="0.0" minY="18" maxX="0.0" maxY="0.0"/>
@ -81,7 +81,7 @@
</connections>
</button>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="5" contentMode="left" misplaced="YES" text="Contact1" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" id="40" userLabel="addressLabel">
<rect key="frame" x="133" y="0.0" width="253" height="66"/>
<rect key="frame" x="75" y="0.0" width="142" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
<accessibility key="accessibilityConfiguration" label="Contact name">
<accessibilityTraits key="traits" none="YES"/>
@ -91,7 +91,7 @@
<nil key="highlightedColor"/>
</label>
<button opaque="NO" tag="6" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="Wzg-i0-spp" userLabel="callButton" customClass="UIIconButton">
<rect key="frame" x="400" y="0.0" width="134" height="66"/>
<rect key="frame" x="225" y="0.0" width="75" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
<state key="normal" image="call_alt_start_default.png">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@ -103,7 +103,7 @@
</connections>
</button>
<button hidden="YES" opaque="NO" tag="7" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="Hc0-GX-fC5" userLabel="backToCallButton" customClass="UIBackToCallButton">
<rect key="frame" x="400" y="0.0" width="134" height="66"/>
<rect key="frame" x="225" y="0.0" width="75" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
<state key="normal" image="call_back_default.png">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@ -116,7 +116,7 @@
</connections>
</button>
<button hidden="YES" opaque="NO" tag="8" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="Wag-Nx-kd6" userLabel="deleteButton" customClass="UIIconButton">
<rect key="frame" x="534" y="0.0" width="133" height="66"/>
<rect key="frame" x="300" y="0.0" width="75" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
<accessibility key="accessibilityConfiguration" label="Delete all"/>
<inset key="titleEdgeInsets" minX="0.0" minY="18" maxX="0.0" maxY="0.0"/>
@ -130,7 +130,7 @@
</connections>
</button>
<button opaque="NO" tag="9" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="FqM-Ud-i58" userLabel="editButton" customClass="UIIconButton">
<rect key="frame" x="534" y="0.0" width="133" height="66"/>
<rect key="frame" x="300" y="0.0" width="75" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
<accessibility key="accessibilityConfiguration" label="Edit"/>
<inset key="titleEdgeInsets" minX="0.0" minY="18" maxX="0.0" maxY="0.0"/>
@ -145,7 +145,7 @@
</connections>
</button>
<button hidden="YES" opaque="NO" tag="10" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" reversesTitleShadowWhenHighlighted="YES" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" id="c9z-aq-2UP" userLabel="toggleSelectionButton" customClass="UIIconButton">
<rect key="frame" x="400" y="0.0" width="134" height="66"/>
<rect key="frame" x="225" y="0.0" width="75" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
<accessibility key="accessibilityConfiguration" label="Select all"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
@ -161,7 +161,7 @@
</connections>
</button>
<button hidden="YES" opaque="NO" tag="10" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" reversesTitleShadowWhenHighlighted="YES" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" id="Vqb-Un-4xv" userLabel="infoButton" customClass="UIIconButton">
<rect key="frame" x="400" y="0.0" width="134" height="66"/>
<rect key="frame" x="225" y="0.0" width="75" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
<accessibility key="accessibilityConfiguration" label="Select all"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
@ -177,7 +177,7 @@
</connections>
</button>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="5" contentMode="left" misplaced="YES" text="addresses" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" id="Ncq-Zc-X6j" userLabel="participantsLabel">
<rect key="frame" x="133" y="41" width="253" height="25"/>
<rect key="frame" x="75" y="41" width="142" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
<accessibility key="accessibilityConfiguration" label="Contact name">
<accessibilityTraits key="traits" none="YES"/>
@ -189,11 +189,11 @@
</subviews>
</view>
<view tag="12" contentMode="scaleToFill" id="49" userLabel="contentView">
<rect key="frame" x="0.0" y="66" width="667" height="201"/>
<rect key="frame" x="0.0" y="66" width="375" height="493"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES" flexibleMaxY="YES"/>
<subviews>
<tableView clipsSubviews="YES" tag="13" contentMode="scaleToFill" alwaysBounceVertical="YES" style="plain" separatorStyle="none" allowsSelection="NO" allowsSelectionDuringEditing="YES" allowsMultipleSelectionDuringEditing="YES" rowHeight="60" sectionHeaderHeight="22" sectionFooterHeight="22" id="8" userLabel="messagesTableView">
<rect key="frame" x="0.0" y="0.0" width="667" height="113"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="405"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<gestureRecognizers/>
@ -205,11 +205,11 @@
</connections>
</tableView>
<view hidden="YES" tag="14" contentMode="scaleToFill" id="fx4-ao-53M" userLabel="composeIndicatorView">
<rect key="frame" x="0.0" y="113" width="667" height="22"/>
<rect key="frame" x="0.0" y="405" width="375" height="22"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="15" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="%@ is composing..." lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" id="fpY-Fv-ht2" userLabel="composeLabel">
<rect key="frame" x="0.0" y="1" width="667" height="22"/>
<rect key="frame" x="0.0" y="1" width="375" height="22"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<accessibility key="accessibilityConfiguration" label=""/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
@ -219,7 +219,7 @@
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
</view>
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" tag="16" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="No conversation." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="p7C-WH-uR1" userLabel="emptyTableLabel">
<rect key="frame" x="0.0" y="0.0" width="667" height="174"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="427"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
@ -227,11 +227,11 @@
<nil key="highlightedColor"/>
</label>
<view tag="17" contentMode="scaleToFill" id="14" userLabel="messageView">
<rect key="frame" x="0.0" y="135" width="667" height="66"/>
<rect key="frame" x="0.0" y="427" width="375" height="66"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" tag="18" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="color_F.png" id="pGT-LQ-zpg" userLabel="backgroundColor">
<rect key="frame" x="0.0" y="0.0" width="667" height="66"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="66"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</imageView>
<button opaque="NO" tag="19" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="73" userLabel="pictureButton">
@ -248,7 +248,7 @@
</connections>
</button>
<button opaque="NO" tag="21" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="15" userLabel="sendButton">
<rect key="frame" x="602" y="0.0" width="66" height="66"/>
<rect key="frame" x="310" y="0.0" width="66" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" label="Send"/>
<inset key="titleEdgeInsets" minX="0.0" minY="30" maxX="0.0" maxY="0.0"/>
@ -262,7 +262,7 @@
</connections>
</button>
<view tag="20" contentMode="scaleToFill" id="pqa-tg-5ml" userLabel="messageField" customClass="HPGrowingTextView">
<rect key="frame" x="117" y="13" width="433" height="40"/>
<rect key="frame" x="66" y="13" width="243" height="40"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" label="Message field"/>
@ -280,23 +280,23 @@
<point key="canvasLocation" x="228.5" y="-40.5"/>
</view>
<view contentMode="scaleToFill" id="680-UL-sil" userLabel="iphone6MetricsView">
<rect key="frame" x="0.0" y="0.0" width="667" height="375"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<view tag="1" contentMode="scaleToFill" id="VoU-7Q-fgp">
<rect key="frame" x="90" y="42" width="577" height="333"/>
<rect key="frame" x="90" y="42" width="285" height="625"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view tag="2" contentMode="scaleToFill" id="Z3y-hY-5xp" userLabel="topBar">
<rect key="frame" x="0.0" y="0.0" width="577" height="66"/>
<rect key="frame" x="0.0" y="0.0" width="285" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" tag="3" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="color_F.png" id="Uvs-m3-GPj" userLabel="backgroundColor">
<rect key="frame" x="0.0" y="0.0" width="577" height="66"/>
<rect key="frame" x="0.0" y="0.0" width="285" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
</imageView>
<button opaque="NO" tag="4" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" id="N2g-vL-3x8" userLabel="backButton" customClass="UIIconButton">
<rect key="frame" x="0.0" y="0.0" width="67" height="66"/>
<rect key="frame" x="0.0" y="0.0" width="33" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
<accessibility key="accessibilityConfiguration" label="Back"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
@ -311,7 +311,7 @@
</connections>
</button>
<button hidden="YES" opaque="NO" tag="11" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="Bqf-Gg-2Rw" userLabel="cancelButton" customClass="UIIconButton">
<rect key="frame" x="0.0" y="0.0" width="67" height="66"/>
<rect key="frame" x="0.0" y="0.0" width="33" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
<accessibility key="accessibilityConfiguration" label="Cancel"/>
<inset key="titleEdgeInsets" minX="0.0" minY="18" maxX="0.0" maxY="0.0"/>
@ -336,7 +336,7 @@
<nil key="highlightedColor"/>
</label>
<button hidden="YES" opaque="NO" tag="8" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="RDW-7W-25T" userLabel="deleteButton" customClass="UIIconButton">
<rect key="frame" x="508" y="0.0" width="69" height="66"/>
<rect key="frame" x="251" y="0.0" width="34" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
<accessibility key="accessibilityConfiguration" label="Delete all"/>
<inset key="titleEdgeInsets" minX="0.0" minY="18" maxX="0.0" maxY="0.0"/>
@ -350,7 +350,7 @@
</connections>
</button>
<button opaque="NO" tag="9" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="KeL-Ej-92j" userLabel="editButton" customClass="UIIconButton">
<rect key="frame" x="508" y="0.0" width="69" height="66"/>
<rect key="frame" x="251" y="0.0" width="34" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
<accessibility key="accessibilityConfiguration" label="Edit"/>
<inset key="titleEdgeInsets" minX="0.0" minY="18" maxX="0.0" maxY="0.0"/>
@ -365,7 +365,7 @@
</connections>
</button>
<button hidden="YES" opaque="NO" tag="10" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" reversesTitleShadowWhenHighlighted="YES" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" id="uVy-PC-gn9" userLabel="infoButton" customClass="UIIconButton">
<rect key="frame" x="441" y="0.0" width="67" height="66"/>
<rect key="frame" x="218" y="0.0" width="33" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
<accessibility key="accessibilityConfiguration" label="Select all"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
@ -381,7 +381,7 @@
</connections>
</button>
<button opaque="NO" tag="6" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="wag-QV-oUD" userLabel="callButton" customClass="UIIconButton">
<rect key="frame" x="441" y="0.0" width="67" height="66"/>
<rect key="frame" x="218" y="0.0" width="33" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
<state key="normal" image="call_alt_start_default.png">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@ -393,7 +393,7 @@
</connections>
</button>
<button hidden="YES" opaque="NO" tag="7" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="t25-en-4LP" userLabel="backToCallButton" customClass="UIBackToCallButton">
<rect key="frame" x="441" y="0.0" width="67" height="66"/>
<rect key="frame" x="218" y="0.0" width="33" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
<state key="normal" image="call_back_default.png">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@ -405,7 +405,7 @@
</connections>
</button>
<button hidden="YES" opaque="NO" tag="10" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" reversesTitleShadowWhenHighlighted="YES" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" id="4RV-US-Kr1" userLabel="toggleSelectionButton" customClass="UIIconButton">
<rect key="frame" x="441" y="0.0" width="67" height="66"/>
<rect key="frame" x="218" y="0.0" width="33" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
<accessibility key="accessibilityConfiguration" label="Select all"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
@ -433,11 +433,11 @@
</subviews>
</view>
<view tag="12" contentMode="scaleToFill" id="OTt-fc-941" userLabel="contentView">
<rect key="frame" x="0.0" y="66" width="577" height="267"/>
<rect key="frame" x="0.0" y="66" width="285" height="559"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES" flexibleMaxY="YES"/>
<subviews>
<tableView clipsSubviews="YES" tag="13" contentMode="scaleToFill" alwaysBounceVertical="YES" style="plain" separatorStyle="none" allowsSelection="NO" allowsSelectionDuringEditing="YES" allowsMultipleSelectionDuringEditing="YES" rowHeight="60" sectionHeaderHeight="22" sectionFooterHeight="22" id="CU7-Za-RwN" userLabel="messagesTableView">
<rect key="frame" x="0.0" y="0.0" width="577" height="179"/>
<rect key="frame" x="0.0" y="0.0" width="285" height="471"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<gestureRecognizers/>
@ -447,7 +447,7 @@
</connections>
</tableView>
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" tag="16" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="No conversation." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="pzm-tk-LH0" userLabel="emptyTableLabel">
<rect key="frame" x="0.0" y="0.0" width="577" height="209"/>
<rect key="frame" x="0.0" y="0.0" width="285" height="438"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
@ -455,11 +455,11 @@
<nil key="highlightedColor"/>
</label>
<view hidden="YES" tag="14" contentMode="scaleToFill" id="nTf-7h-Z4z" userLabel="composeIndicatorView">
<rect key="frame" x="0.0" y="179" width="577" height="22"/>
<rect key="frame" x="0.0" y="471" width="285" height="22"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="15" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="%@ is composing..." lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" id="I34-aL-yuS" userLabel="composeLabel">
<rect key="frame" x="0.0" y="0.0" width="577" height="22"/>
<rect key="frame" x="0.0" y="0.0" width="285" height="22"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<accessibility key="accessibilityConfiguration" label=""/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
@ -469,11 +469,11 @@
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
</view>
<view tag="17" contentMode="scaleToFill" id="LA5-wD-ftj" userLabel="messageView">
<rect key="frame" x="0.0" y="201" width="577" height="66"/>
<rect key="frame" x="0.0" y="493" width="285" height="66"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" tag="18" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="color_F.png" id="kKc-DG-gwg" userLabel="backgroundColor">
<rect key="frame" x="0.0" y="0.0" width="577" height="66"/>
<rect key="frame" x="0.0" y="0.0" width="285" height="66"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</imageView>
<button opaque="NO" tag="19" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="gSL-jE-GYO" userLabel="pictureButton">
@ -490,13 +490,13 @@
</connections>
</button>
<view tag="20" contentMode="scaleToFill" id="C02-2r-vXK" userLabel="messageField" customClass="HPGrowingTextView">
<rect key="frame" x="59" y="13" width="457" height="40"/>
<rect key="frame" x="29" y="13" width="226" height="40"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" label="Message field"/>
</view>
<button opaque="NO" tag="21" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="nV9-xZ-oSM" userLabel="sendButton">
<rect key="frame" x="511" y="0.0" width="66" height="66"/>
<rect key="frame" x="219" y="0.0" width="66" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" label="Send"/>
<inset key="titleEdgeInsets" minX="0.0" minY="30" maxX="0.0" maxY="0.0"/>

View file

@ -28,6 +28,7 @@
@protocol ChatConversationDelegate <NSObject>
- (BOOL)startImageUpload:(UIImage *)image url:(NSURL *)url withQuality:(float)quality;
- (BOOL)startFileUpload:(NSData *)data withUrl:(NSURL *)url;
- (void)resendChat:(NSString *)message withExternalUrl:(NSString *)url;
- (void)tableViewIsScrolling;

View file

@ -33,6 +33,7 @@
[self clearEventList];
}
#pragma mark - ViewController Functions
- (void)viewWillAppear:(BOOL)animated {
@ -173,15 +174,15 @@
LinphoneEventLog *event = [[eventList objectAtIndex:indexPath.row] pointerValue];
if (linphone_event_log_get_type(event) == LinphoneEventLogTypeConferenceChatMessage) {
LinphoneChatMessage *chat = linphone_event_log_get_chat_message(event);
if (linphone_chat_message_get_file_transfer_information(chat) || linphone_chat_message_get_external_body_url(chat))
kCellId = NSStringFromClass(UIChatBubblePhotoCell.class);
if (linphone_chat_message_get_file_transfer_information(chat) || linphone_chat_message_get_external_body_url(chat))
kCellId = NSStringFromClass(UIChatBubblePhotoCell.class);
else
kCellId = NSStringFromClass(UIChatBubbleTextCell.class);
UIChatBubbleTextCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellId];
if (!cell)
if (!cell) {
cell = [[NSClassFromString(kCellId) alloc] initWithIdentifier:kCellId];
}
[cell setEvent:event];
if (chat)
[cell update];
@ -209,14 +210,34 @@
[_chatRoomDelegate tableViewIsScrolling];
}
static const CGFloat MESSAGE_SPACING_PERCENTAGE = 5.f;
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
LinphoneEventLog *event = [[eventList objectAtIndex:indexPath.row] pointerValue];
if (linphone_event_log_get_type(event) == LinphoneEventLogTypeConferenceChatMessage) {
LinphoneChatMessage *chat = linphone_event_log_get_chat_message(event);
return [UIChatBubbleTextCell ViewHeightForMessage:chat withWidth:self.view.frame.size.width].height;
} else {
return [UIChatNotifiedEventCell height];
LinphoneChatMessage *chat = linphone_event_log_get_chat_message(event);
//If the message is followed by another one that is not from the same address, we add a little space under it
CGFloat height = 0;
LinphoneEventLog *nextEvent = nil;
NSInteger indexOfNextEvent = indexPath.row + 1;
while (!nextEvent && indexOfNextEvent < [eventList count]) {
LinphoneEventLog *tmp = [[eventList objectAtIndex:indexOfNextEvent] pointerValue];
if (linphone_event_log_get_type(tmp) == LinphoneEventLogTypeConferenceChatMessage) {
nextEvent = tmp;
}
++indexOfNextEvent;
}
if (nextEvent) {
LinphoneChatMessage *nextChat = linphone_event_log_get_chat_message(nextEvent);
if (!linphone_address_equal(linphone_chat_message_get_from_address(nextChat), linphone_chat_message_get_from_address(chat))) {
LOGD(@"BITE");
height += tableView.frame.size.height * MESSAGE_SPACING_PERCENTAGE / 100;
}
}
return [UIChatBubbleTextCell ViewHeightForMessage:chat withWidth:self.view.frame.size.width].height + height;
}
return [UIChatNotifiedEventCell height];
}
- (void) tableView:(UITableView *)tableView deleteRowAtIndex:(NSIndexPath *)indexPath {

View file

@ -31,8 +31,8 @@
#include "linphone/linphonecore.h"
@interface ChatConversationView
: TPMultiLayoutViewController <HPGrowingTextViewDelegate, UICompositeViewDelegate, ImagePickerDelegate,
ChatConversationDelegate, UISearchBarDelegate> {
: TPMultiLayoutViewController <HPGrowingTextViewDelegate, UICompositeViewDelegate, ImagePickerDelegate, ChatConversationDelegate,
UIDocumentInteractionControllerDelegate, UISearchBarDelegate> {
OrderedDictionary *imageQualities;
BOOL scrollOnGrowingEnabled;
BOOL composingVisible;
@ -40,6 +40,7 @@
@property(nonatomic) LinphoneChatRoom *chatRoom;
@property(nonatomic) LinphoneChatRoomCbs *chatRoomCbs;
@property(nonatomic) Boolean markAsRead;
@property(weak, nonatomic) IBOutlet UIIconButton *backButton;
@property(nonatomic, strong) IBOutlet ChatConversationTableView *tableController;
@ -58,7 +59,7 @@
@property(weak, nonatomic) IBOutlet UIBackToCallButton *backToCallButton;
@property (weak, nonatomic) IBOutlet UIIconButton *infoButton;
@property (weak, nonatomic) IBOutlet UILabel *particpantsLabel;
@property (nonatomic, strong) UIDocumentInteractionController *documentInteractionController;
+ (void)markAsRead:(LinphoneChatRoom *)chatRoom;
- (void)configureForRoom:(BOOL)editing;
@ -72,5 +73,6 @@
- (IBAction)onDeleteClick:(id)sender;
- (IBAction)onEditionChangeClick:(id)sender;
- (void)update;
- (void)openResults:(NSString *) filePath;
@end

View file

@ -24,6 +24,7 @@
#import "UIChatBubbleTextCell.h"
@implementation ChatConversationView
static NSString* groupName = @"group.belledonne-communications.linphone";
#pragma mark - Lifecycle Functions
@ -84,7 +85,7 @@ static UICompositeViewDescription *compositeDescription = nil;
- (void)viewDidLoad {
[super viewDidLoad];
_markAsRead = TRUE;
// if we use fragments, remove back button
if (IPAD) {
_backButton.hidden = YES;
@ -205,11 +206,51 @@ static UICompositeViewDescription *compositeDescription = nil;
_chatView.hidden = NO;
[self update];
[self shareFile];
}
- (void)shareFile {
NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:groupName];
NSDictionary *dict = [defaults valueForKey:@"img"];
NSDictionary *dictWeb = [defaults valueForKey:@"web"];
NSDictionary *dictFile = [defaults valueForKey:@"mov"];
NSDictionary *dictText = [defaults valueForKey:@"text"];
if (dict) {
//share photo
NSData *data = dict[@"nsData"];
UIImage *image = [[UIImage alloc] initWithData:data];
[self chooseImageQuality:image url:nil];
[defaults removeObjectForKey:@"img"];
} else if (dictWeb) {
//share url, if local file, then upload file
NSString *url = dictWeb[@"url"];
NSURL *fileUrl = [NSURL fileURLWithPath:url];
if ([url hasPrefix:@"file"]) {
//local file
NSData *data = dictWeb[@"nsData"];
[self confirmShare:data url:fileUrl text:nil];
} else {
[self confirmShare:nil url:nil text:url];
}
[defaults removeObjectForKey:@"web"];
}else if (dictFile) {
//share file
NSData *data = dictFile[@"nsData"];
[self confirmShare:data url:[NSURL fileURLWithPath:dictFile[@"url"]] text:nil];
[defaults removeObjectForKey:@"mov"];
}else if (dictText) {
//share text
[self confirmShare:nil url:nil text:dictText[@"name"]];
[defaults removeObjectForKey:@"text"];
}
}
- (void)applicationWillEnterForeground:(NSNotification *)notif {
if (_chatRoom)
if (_chatRoom && _markAsRead)
[ChatConversationView markAsRead:_chatRoom];
_markAsRead = TRUE;
}
- (void)callUpdateEvent:(NSNotification *)notif {
@ -253,8 +294,8 @@ static UICompositeViewDescription *compositeDescription = nil;
}
if (internalUrl) {
// internal url is saved in the appdata for display and later save
[LinphoneManager setValueInMessageAppData:[internalUrl absoluteString] forKey:@"localimage" inMessage:msg];
// internal url is saved in the appdata for display and later save
[LinphoneManager setValueInMessageAppData:[internalUrl absoluteString] forKey:@"localimage" inMessage:msg];
}
// we must ref & unref message because in case of error, it will be destroy otherwise
@ -318,6 +359,26 @@ static UICompositeViewDescription *compositeDescription = nil;
});
}
- (void)confirmShare:(NSData *)data url:(NSURL *)url text:(NSString *)text {
DTActionSheet *sheet = [[DTActionSheet alloc] initWithTitle:NSLocalizedString(@"", nil)];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[sheet addButtonWithTitle:@"send to this friend"
block:^() {
if(data && url)
[self startFileUpload:data withUrl:url];
else
[self sendMessage:text withExterlBodyUrl:nil withInternalURL:nil];
}];
[sheet addCancelButtonWithTitle:NSLocalizedString(@"Cancel", nil) block:nil];
dispatch_async(dispatch_get_main_queue(), ^{
[sheet showInView:PhoneMainView.instance.view];
});
});
}
- (void)setComposingVisible:(BOOL)visible withDelay:(CGFloat)delay {
Boolean shouldAnimate = composingVisible != visible;
CGRect keyboardFrame = [_messageView frame];
@ -547,6 +608,13 @@ static UICompositeViewDescription *compositeDescription = nil;
return TRUE;
}
- (BOOL)startFileUpload:(NSData *)data withUrl:(NSURL *)url {
FileTransferDelegate *fileTransfer = [[FileTransferDelegate alloc] init];
[fileTransfer uploadFile:data forChatRoom:_chatRoom withUrl:url];
[_tableController scrollToBottom:true];
return TRUE;
}
- (void)resendChat:(NSString *)message withExternalUrl:(NSString *)url {
[self sendMessage:message withExterlBodyUrl:[NSURL URLWithString:url] withInternalURL:nil];
}
@ -760,6 +828,7 @@ void on_chat_room_chat_message_sent(LinphoneChatRoom *cr, const LinphoneEventLog
ChatConversationView *view = (__bridge ChatConversationView *)linphone_chat_room_cbs_get_user_data(linphone_chat_room_get_current_callbacks(cr));
[view.tableController addEventEntry:(LinphoneEventLog *)event_log];
[view.tableController scrollToBottom:true];
[ChatsListTableView saveDataToUserDefaults];
if (IPAD)
[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneMessageReceived object:view];
@ -783,4 +852,18 @@ void on_chat_room_conference_left(LinphoneChatRoom *cr, const LinphoneEventLog *
[view.tableController scrollToBottom:true];
}
- (void)openResults:(NSString *) filePath
{
// Open the controller.
_documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:filePath]];
_documentInteractionController.delegate = self;
BOOL canOpen = [_documentInteractionController presentOpenInMenuFromRect:CGRectZero inView:self.view animated:YES];
//NO app can open the file
if (canOpen == NO) {
[[[UIAlertView alloc] initWithTitle:@"Info" message:@"There is no app found to open it" delegate:nil cancelButtonTitle:@"cancel" otherButtonTitles:nil, nil] show];
}
}
@end

View file

@ -30,4 +30,5 @@
- (void)loadData;
- (void)markCellAsRead:(LinphoneChatRoom *)chatRoom;
+ (void)saveDataToUserDefaults;
@end

View file

@ -123,6 +123,55 @@ static int sorted_history_comparison(LinphoneChatRoom *to_insert, LinphoneChatRo
}
}
+ (void) saveDataToUserDefaults {
NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.belledonne-communications.linphone.widget"];
MSList *sorted = nil;
const MSList *unsorted = linphone_core_get_chat_rooms(LC);
const MSList *iter = unsorted;
while (iter) {
// store last message in user data
LinphoneChatRoom *chat_room = iter->data;
sorted = bctbx_list_insert_sorted(sorted, chat_room, (bctbx_compare_func)sorted_history_comparison);
iter = iter->next;
}
NSMutableArray *addresses = [NSMutableArray array];
while (sorted) {
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
LinphoneChatRoom *cr = sorted->data;
const LinphoneAddress *peer_address = linphone_chat_room_get_peer_address(cr);
const LinphoneAddress *local_address = linphone_chat_room_get_local_address(cr);
NSString *display;
[dict setObject:[NSString stringWithUTF8String:linphone_address_as_string_uri_only(peer_address)]
forKey:@"peer"];
[dict setObject:[NSString stringWithUTF8String:linphone_address_as_string_uri_only(local_address)]
forKey:@"local"];
if (linphone_chat_room_get_conference_address(cr))
display = [NSString stringWithUTF8String:linphone_chat_room_get_subject(cr)];
else {
display = [NSString stringWithUTF8String:linphone_address_get_display_name(peer_address)?:linphone_address_get_username(peer_address)];
if ([FastAddressBook imageForAddress:peer_address])
[dict setObject:UIImageJPEGRepresentation([UIImage resizeImage:[FastAddressBook imageForAddress:peer_address]
withMaxWidth:200
andMaxHeight:200],
1)
forKey:@"img"];
}
[dict setObject:display
forKey:@"display"];
[dict setObject:[NSNumber numberWithBool:linphone_chat_room_get_conference_address(cr)]
forKey:@"nbParticipants"];
[addresses addObject:dict];
if (addresses.count >= 4) //send no more data than needed
break;
sorted = sorted->next;
}
[defaults setObject:addresses forKey:@"chatrooms"];
}
- (void)markCellAsRead:(LinphoneChatRoom *)chatRoom {
int idx = bctbx_list_index(_data, VIEW(ChatConversationView).chatRoom);
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:idx inSection:0];

View file

@ -29,4 +29,5 @@
@property(strong, nonatomic) NSMutableDictionary *sections;
@property(strong, nonatomic) NSMutableArray *sortedDays;
+ (void) saveDataToUserDefaults;
@end

View file

@ -158,7 +158,7 @@
[self computeSections];
[super loadData];
if (IPAD) {
if (![self selectFirstRow]) {
HistoryDetailsView *view = VIEW(HistoryDetailsView);
@ -167,6 +167,64 @@
}
}
+ (void) saveDataToUserDefaults {
const bctbx_list_t *logs = linphone_core_get_call_logs(LC);
NSUserDefaults *mySharedDefaults = [[NSUserDefaults alloc] initWithSuiteName: @"group.belledonne-communications.linphone.widget"];
NSMutableArray *logsShare = [NSMutableArray array];
NSMutableDictionary *tmpStoreDict = [NSMutableDictionary dictionary];
NSMutableArray *addedContacts = [NSMutableArray array];
while (logs) {
LinphoneCallLog *log = (LinphoneCallLog *)logs->data;
const LinphoneAddress *address = linphone_call_log_get_remote_address(log);
// if contact is already to be display, skip
if ([addedContacts containsObject:[NSString stringWithUTF8String:linphone_address_as_string_uri_only(address)]]) {
logs = bctbx_list_next(logs);
continue;
}
// if null log id, skip
if (!linphone_call_log_get_call_id(log)) {
logs = bctbx_list_next(logs);
continue;
}
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject:[NSString stringWithUTF8String:linphone_call_log_get_call_id(log)]
forKey:@"id"];
[dict setObject:[NSString stringWithUTF8String:linphone_address_get_display_name(address)?:linphone_address_get_username(address)]
forKey:@"display"];
UIImage *avatar = [FastAddressBook imageForAddress:address];
if (avatar) {
UIImage *image = [UIImage resizeImage:avatar
withMaxWidth:200
andMaxHeight:200];
NSData *imageData = UIImageJPEGRepresentation(image, 1);
[dict setObject:imageData
forKey:@"img"];
}
[tmpStoreDict setObject:dict
forKey:[NSDate dateWithTimeIntervalSince1970:linphone_call_log_get_start_date(log)]];
[addedContacts addObject:[NSString stringWithUTF8String:linphone_address_as_string_uri_only(address)]];
logs = bctbx_list_next(logs);
}
NSArray *sortedDates = [[NSMutableArray alloc]
initWithArray:[tmpStoreDict.allKeys sortedArrayUsingComparator:^NSComparisonResult(NSDate *d1, NSDate *d2) {
return [d2 compare:d1];
}]];
// sort logs array on date
for (NSDate *date in sortedDates) {
[logsShare addObject:[tmpStoreDict objectForKey:date]];
if (logsShare.count >= 4) //send no more data than needed
break;
}
[mySharedDefaults setObject:logsShare forKey:@"logs"];
}
- (void)computeSections {
NSArray *unsortedDays = [self.sections allKeys];
_sortedDays = [[NSMutableArray alloc]

View file

@ -37,4 +37,4 @@
- (IBAction)onBackClick:(id)sender;
@end
@end

View file

@ -39,6 +39,8 @@
@property PKPushRegistry* voipRegistry;
@property ProviderDelegate *del;
@property BOOL alreadyRegisteredForNotification;
@property BOOL onlyPortrait;
@property UIApplicationShortcutItem *shortcutItem;
@end

View file

@ -44,6 +44,7 @@
startedInBackground = FALSE;
}
_alreadyRegisteredForNotification = false;
_onlyPortrait = FALSE;
return self;
[[UIApplication sharedApplication] setDelegate:self];
}
@ -135,6 +136,12 @@
}
}
[LinphoneManager.instance.iapManager check];
if (_shortcutItem) {
[self handleShortcut:_shortcutItem];
_shortcutItem = nil;
}
[HistoryListTableView saveDataToUserDefaults];
[ChatsListTableView saveDataToUserDefaults];
}
#pragma deploymate push "ignored-api-availability"
@ -223,6 +230,7 @@
if (error)
LOGD(error.description);
}];
NSSet *categories = [NSSet setWithObjects:cat_call, cat_msg, video_call, cat_zrtp, nil];
[[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:categories];
}
@ -279,6 +287,12 @@
//output what state the app is in. This will be used to see when the app is started in the background
LOGI(@"app launched with state : %li", (long)application.applicationState);
LOGI(@"FINISH LAUNCHING WITH OPTION : %@", launchOptions.description);
UIApplicationShortcutItem *shortcutItem = [launchOptions objectForKey:@"UIApplicationLaunchOptionsShortcutItemKey"];
if (shortcutItem) {
_shortcutItem = shortcutItem;
return NO;
}
return YES;
}
@ -308,6 +322,19 @@
[LinphoneManager.instance destroyLinphoneCore];
}
- (BOOL)handleShortcut:(UIApplicationShortcutItem *)shortcutItem {
BOOL success = NO;
if ([shortcutItem.type isEqualToString:@"linphone.phone.action.newMessage"]) {
[PhoneMainView.instance changeCurrentView:ChatConversationCreateView.compositeViewDescription];
success = YES;
}
return success;
}
- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler {
completionHandler([self handleShortcut:shortcutItem]);
}
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
NSString *scheme = [[url scheme] lowercaseString];
if ([scheme isEqualToString:@"linphone-config"] || [scheme isEqualToString:@"linphone-config"]) {
@ -333,16 +360,41 @@
[errView addAction:yesAction];
[PhoneMainView.instance presentViewController:errView animated:YES completion:nil];
} else {
if ([[url scheme] isEqualToString:@"sip"]) {
// remove "sip://" from the URI, and do it correctly by taking resourceSpecifier and removing leading and
// trailing "/"
NSString *sipUri = [[url resourceSpecifier]
stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"/"]];
[VIEW(DialerView) setAddress:sipUri];
}
}
return YES;
} else if([[url scheme] isEqualToString:@"message-linphone"]) {
[PhoneMainView.instance popToView:ChatsListView.compositeViewDescription];
} else if ([scheme isEqualToString:@"sip"]) {
// remove "sip://" from the URI, and do it correctly by taking resourceSpecifier and removing leading and
// trailing "/"
NSString *sipUri = [[url resourceSpecifier] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"/"]];
[VIEW(DialerView) setAddress:sipUri];
} else if ([scheme isEqualToString:@"linphone-widget"]) {
if ([[url host] isEqualToString:@"call_log"] &&
[[url path] isEqualToString:@"/show"]) {
[VIEW(HistoryDetailsView) setCallLogId:[url query]];
[PhoneMainView.instance changeCurrentView:HistoryDetailsView.compositeViewDescription];
} else if ([[url host] isEqualToString:@"chatroom"] && [[url path] isEqualToString:@"/show"]) {
NSURLComponents *urlComponents = [NSURLComponents componentsWithURL:url
resolvingAgainstBaseURL:NO];
NSArray *queryItems = urlComponents.queryItems;
NSString *peerAddress = [self valueForKey:@"peer" fromQueryItems:queryItems];
NSString *localAddress = [self valueForKey:@"local" fromQueryItems:queryItems];
LinphoneAddress *peer = linphone_address_new(peerAddress.UTF8String);
LinphoneAddress *local = linphone_address_new(localAddress.UTF8String);
LinphoneChatRoom *cr = linphone_core_find_chat_room(LC, peer, local);
linphone_address_unref(peer);
linphone_address_unref(local);
// TODO : Find a better fix
VIEW(ChatConversationView).markAsRead = FALSE;
[PhoneMainView.instance goToChatRoom:cr];
}
}
return YES;
}
- (NSString *)valueForKey:(NSString *)key fromQueryItems:(NSArray *)queryItems {
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name=%@", key];
NSURLQueryItem *queryItem = [[queryItems filteredArrayUsingPredicate:predicate] firstObject];
return queryItem.value;
}
- (void)fixRing {
@ -659,7 +711,7 @@
}];
} else if ([response.notification.request.content.categoryIdentifier isEqual:@"lime"]) {
return;
} else { // Missed call
} else { // Missed call
[PhoneMainView.instance changeCurrentView:HistoryListView.compositeViewDescription];
}
}
@ -830,14 +882,13 @@
#pragma mark - Prevent ImagePickerView from rotating
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
if ([[(PhoneMainView*)self.window.rootViewController currentView] equal:ImagePickerView.compositeViewDescription])
if ([[(PhoneMainView*)self.window.rootViewController currentView] equal:ImagePickerView.compositeViewDescription] || _onlyPortrait)
{
//Prevent rotation of camera
NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationPortrait];
[[UIDevice currentDevice] setValue:value forKey:@"orientation"];
return UIInterfaceOrientationMaskPortrait;
}
else return UIInterfaceOrientationMaskAllButUpsideDown;
} else return UIInterfaceOrientationMaskAllButUpsideDown;
}
@end

View file

@ -56,6 +56,7 @@ extern NSString *const kLinphoneNotifyPresenceReceivedForUriOrTel;
extern NSString *const kLinphoneCallEncryptionChanged;
extern NSString *const kLinphoneFileTransferSendUpdate;
extern NSString *const kLinphoneFileTransferRecvUpdate;
extern NSString *const kLinphoneQRCodeFound;
typedef enum _NetworkType {
network_none = 0,
@ -205,6 +206,8 @@ typedef struct _LinphoneManagerSounds {
- (void) setContactsUpdated:(BOOL) updated;
- (BOOL) getContactsUpdated;
- (void)checkNewVersion;
@property ProviderDelegate *providerDelegate;
@property (readonly) BOOL isTesting;

View file

@ -76,6 +76,7 @@ NSString *const kLinphoneNotifyPresenceReceivedForUriOrTel = @"LinphoneNotifyPre
NSString *const kLinphoneCallEncryptionChanged = @"LinphoneCallEncryptionChanged";
NSString *const kLinphoneFileTransferSendUpdate = @"LinphoneFileTransferSendUpdate";
NSString *const kLinphoneFileTransferRecvUpdate = @"LinphoneFileTransferRecvUpdate";
NSString *const kLinphoneQRCodeFound = @"LinphoneQRCodeFound";
const int kLinphoneAudioVbrCodecDefaultBitrate = 36; /*you can override this from linphonerc or linphonerc-factory*/
@ -718,6 +719,7 @@ static void linphone_iphone_display_status(struct _LinphoneCore *lc, const char
// Disable speaker when no more call
if ((state == LinphoneCallEnd || state == LinphoneCallError)) {
[HistoryListTableView saveDataToUserDefaults];
[[UIDevice currentDevice] setProximityMonitoringEnabled:FALSE];
speaker_already_enabled = FALSE;
if (linphone_core_get_calls_nb(theLinphoneCore) == 0) {
@ -1041,18 +1043,18 @@ static void linphone_iphone_popup_password_request(LinphoneCore *lc, LinphoneAut
@"provide password again, or check your "
@"account configuration in the settings.", nil), username, realm]
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", nil)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {}];
[alertView addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.placeholder = NSLocalizedString(@"Password", nil);
textField.clearButtonMode = UITextFieldViewModeWhileEditing;
textField.borderStyle = UITextBorderStyleRoundedRect;
textField.secureTextEntry = YES;
}];
UIAlertAction* continueAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"Confirm password", nil)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
@ -1063,13 +1065,13 @@ static void linphone_iphone_popup_password_request(LinphoneCore *lc, LinphoneAut
linphone_core_add_auth_info(LC, info);
[LinphoneManager.instance refreshRegisters];
}];
UIAlertAction* settingsAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"Go to settings", nil)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[PhoneMainView.instance changeCurrentView:SettingsView.compositeViewDescription];
}];
[alertView addAction:defaultAction];
[alertView addAction:continueAction];
[alertView addAction:settingsAction];
@ -1145,7 +1147,7 @@ static void linphone_iphone_popup_password_request(LinphoneCore *lc, LinphoneAut
if (PhoneMainView.instance.currentView == ChatConversationView.compositeViewDescription && room == PhoneMainView.instance.currentRoom)
return;
}
// Create a new notification
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) {
NSArray *actions;
@ -1241,6 +1243,8 @@ static void linphone_iphone_popup_password_request(LinphoneCore *lc, LinphoneAut
}
}];
}
[ChatsListTableView saveDataToUserDefaults];
[HistoryListTableView saveDataToUserDefaults];
}
static void linphone_iphone_message_received(LinphoneCore *lc, LinphoneChatRoom *room, LinphoneChatMessage *message) {
@ -1394,6 +1398,34 @@ void linphone_iphone_chatroom_state_changed(LinphoneCore *lc, LinphoneChatRoom *
[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneMessageReceived object:nil];
}
void linphone_iphone_version_update_check_result_received (LinphoneCore *lc, LinphoneVersionUpdateCheckResult result, const char *version, const char *url) {
if (result == LinphoneVersionUpdateCheckUpToDate || result == LinphoneVersionUpdateCheckError) {
return;
}
NSString *title = NSLocalizedString(@"Outdated Version", nil);
NSString *body = NSLocalizedString(@"A new version of your app is available, use the button below to download it.", nil);
UIAlertController *versVerifView = [UIAlertController alertControllerWithTitle:title
message:body
preferredStyle:UIAlertControllerStyleAlert];
NSString *ObjCurl = [NSString stringWithUTF8String:url];
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"Download", nil)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:ObjCurl]];
}];
[versVerifView addAction:defaultAction];
[PhoneMainView.instance presentViewController:versVerifView animated:YES completion:nil];
}
void linphone_iphone_qr_code_found(LinphoneCore *lc, const char *result) {
NSDictionary *eventDic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:result encoding:[NSString defaultCStringEncoding]] forKey:@"qrcode"];
LOGD(@"QRCODE FOUND");
[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneQRCodeFound object:nil userInfo:eventDic];
}
#pragma mark - Message composition start
- (void)alertLIME:(LinphoneChatRoom *)room {
NSString *title = NSLocalizedString(@"LIME warning", nil);
@ -1572,7 +1604,7 @@ static void networkReachabilityNotification(CFNotificationCenterRef center, void
if ([newSSID compare:mgr.SSID] == NSOrderedSame)
return;
if (newSSID != Nil && newSSID.length > 0 && mgr.SSID != Nil && newSSID.length > 0) {
if (SCNetworkReachabilityGetFlags([mgr getProxyReachability], &flags)) {
LOGI(@"Wifi SSID changed, resesting transports.");
@ -1798,10 +1830,13 @@ void networkReachabilityCallBack(SCNetworkReachabilityRef target, SCNetworkReach
linphone_core_enable_video_display(theLinphoneCore, FALSE);
linphone_core_enable_video_capture(theLinphoneCore, FALSE);
}
[self enableProxyPublish:([UIApplication sharedApplication].applicationState == UIApplicationStateActive)];
LOGI(@"Linphone [%s] started on [%s]", linphone_core_get_version(), [[UIDevice currentDevice].model UTF8String]);
//update UserDefaults for widgets
[HistoryListTableView saveDataToUserDefaults];
LOGI(@"Linphone [%s] started on [%s]", linphone_core_get_version(), [[UIDevice currentDevice].model UTF8String]);
// Post event
NSDictionary *dict = [NSDictionary dictionaryWithObject:[NSValue valueWithPointer:theLinphoneCore] forKey:@"core"];
@ -1847,11 +1882,11 @@ static BOOL libStarted = FALSE;
UIAlertController *errView = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"No microphone", nil)
message:NSLocalizedString(@"You need to plug a microphone to your device to use the application.", nil)
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {}];
[errView addAction:defaultAction];
[PhoneMainView.instance presentViewController:errView animated:YES completion:nil];
}
@ -1877,11 +1912,11 @@ void popup_link_account_cb(LinphoneAccountCreator *creator, LinphoneAccountCreat
message:[NSString stringWithFormat:NSLocalizedString(@"Link your Linphone.org account %s to your phone number.", nil),
linphone_address_get_username(linphone_proxy_config_get_identity_address(cfg))]
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"Maybe later", nil)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {}];
UIAlertAction* continueAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"Let's go", nil)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
@ -1929,7 +1964,7 @@ void popup_link_account_cb(LinphoneAccountCreator *creator, LinphoneAccountCreat
LOGI(@"linphonecore is already created");
return;
}
connectivity = none;
// Set audio assets
@ -1964,11 +1999,14 @@ void popup_link_account_cb(LinphoneAccountCreator *creator, LinphoneAccountCreat
linphone_core_cbs_set_notify_received(cbs, linphone_iphone_notify_received);
linphone_core_cbs_set_call_encryption_changed(cbs, linphone_iphone_call_encryption_changed);
linphone_core_cbs_set_chat_room_state_changed(cbs, linphone_iphone_chatroom_state_changed);
linphone_core_cbs_set_version_update_check_result_received(cbs, linphone_iphone_version_update_check_result_received);
linphone_core_cbs_set_qrcode_found(cbs, linphone_iphone_qr_code_found);
linphone_core_cbs_set_user_data(cbs, (__bridge void *)(self));
theLinphoneCore = linphone_factory_create_core_with_config_3(factory, _configDb, NULL);
linphone_core_add_callbacks(theLinphoneCore, cbs);
linphone_core_start(theLinphoneCore);
// Let the core handle cbs
linphone_core_cbs_unref(cbs);
@ -1982,7 +2020,7 @@ void popup_link_account_cb(LinphoneAccountCreator *creator, LinphoneAccountCreat
libmsopenh264_init(f);
libmswebrtc_init(f);
libmscodec2_init(f);
linphone_core_reload_ms_plugins(theLinphoneCore, NULL);
[self migrationAllPost];
@ -2248,7 +2286,7 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) {
if (linphone_core_get_global_state(LC) != LinphoneGlobalOn || !linphone_core_get_default_friend_list(LC)) {
LOGW(@"Not changing presence configuration because linphone core not ready yet");
return;
}
}
if ([self lpConfigBoolForKey:@"publish_presence"]) {
// set present to "tv", because "available" does not work yet
@ -2283,7 +2321,7 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) {
// disable presence
[self enableProxyPublish:NO];
// handle proxy config if any
if (proxyCfg) {
const char *refkey = proxyCfg ? linphone_proxy_config_get_ref_key(proxyCfg) : NULL;
@ -2336,6 +2374,8 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) {
- (void)becomeActive {
linphone_core_enter_foreground(LC);
[self checkNewVersion];
// enable presence
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max || self.connectivity == none) {
[self refreshRegisters];
@ -2563,11 +2603,11 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) {
UIAlertController *errView = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Network Error", nil)
message:NSLocalizedString(@"There is no network connection available, enable WIFI or WWAN prior to place a call", nil)
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {}];
[errView addAction:defaultAction];
[PhoneMainView.instance presentViewController:errView animated:YES completion:nil];
return;
@ -2596,11 +2636,11 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) {
message:NSLocalizedString(@"Either configure a SIP proxy server from settings prior to place a "
@"call or use a valid SIP address (I.E sip:john@example.net)", nil)
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {}];
[errView addAction:defaultAction];
[PhoneMainView.instance presentViewController:errView animated:YES completion:nil];
return;
@ -3026,4 +3066,14 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) {
- (void)exportSymbolsForUITests {
linphone_address_set_header(NULL, NULL, NULL);
}
- (void)checkNewVersion {
if (!CHECK_VERSION_UPDATE)
return;
if (theLinphoneCore == nil)
return;
NSString *curVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
const char *curVersionCString = [curVersion cStringUsingEncoding:NSUTF8StringEncoding];
linphone_core_check_for_update(theLinphoneCore, curVersionCString);
}
@end

View file

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="9060" systemVersion="15B42" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9051"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="ImageView">
<connections>
<outlet property="backButton" destination="RW1-kp-wn7" id="DJc-Ps-J3p"/>
<outlet property="scrollView" destination="12" id="13"/>
<outlet property="view" destination="1" id="3"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="zEp-6r-r9n" userLabel="iphone6MetricsView">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<view contentMode="scaleToFill" id="1">
<rect key="frame" x="0.0" y="42" width="375" height="559"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" id="2E4-s5-jYL" userLabel="topBar">
<rect key="frame" x="0.0" y="0.0" width="375" height="66"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="color_F.png" id="Rir-PV-D7o" userLabel="backgroundColor">
<rect key="frame" x="0.0" y="0.0" width="375" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
<animations/>
</imageView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" id="RW1-kp-wn7" userLabel="backButton">
<rect key="frame" x="0.0" y="0.0" width="75" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
<animations/>
<accessibility key="accessibilityConfiguration" label="New Discussion"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<inset key="titleEdgeInsets" minX="0.0" minY="18" maxX="0.0" maxY="0.0"/>
<state key="normal" image="back_default.png">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<state key="disabled" image="back_disabled.png"/>
<state key="highlighted" backgroundImage="color_E.png"/>
<connections>
<action selector="onBackClick:" destination="-1" eventType="touchUpInside" id="vyb-kn-xSQ"/>
</connections>
</button>
</subviews>
<animations/>
</view>
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" minimumZoomScale="0.0" maximumZoomScale="10" id="12" userLabel="scrollView" customClass="UIImageScrollView">
<rect key="frame" x="0.0" y="66" width="375" height="493"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<animations/>
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
</scrollView>
</subviews>
<animations/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
</subviews>
<animations/>
<color key="backgroundColor" red="1" green="1" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<simulatedScreenMetrics key="simulatedDestinationMetrics" type="retina47"/>
</view>
</objects>
<resources>
<image name="back_default.png" width="24" height="21"/>
<image name="back_disabled.png" width="24" height="21"/>
<image name="color_E.png" width="2" height="2"/>
<image name="color_F.png" width="2" height="2"/>
</resources>
</document>

View file

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13527"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@ -15,16 +15,20 @@
<outlet property="avatarImage" destination="hD2-19-7IH" id="N1B-Jn-wTo"/>
<outlet property="backgroundColorImage" destination="U2P-5n-gg8" id="1Hn-bx-mua"/>
<outlet property="bottomBarColor" destination="6dA-3U-OPW" id="xJX-pe-zlu"/>
<outlet property="bubbleView" destination="UGz-WT-BUv" id="8qr-kw-dSx"/>
<outlet property="bubbleView" destination="UGz-WT-BUv" id="Usm-sg-420"/>
<outlet property="cancelButton" destination="6dl-Nz-rdv" id="ygz-nv-omC"/>
<outlet property="contactDateLabel" destination="JyR-RQ-uwF" id="Tc4-9t-i5V"/>
<outlet property="downloadButton" destination="N75-gL-R6t" id="EgN-Ab-Ded"/>
<outlet property="fileName" destination="Dho-UV-6Ev" id="Iro-II-w8a"/>
<outlet property="fileTransferProgress" destination="USm-wC-GvG" id="POt-YD-NCG"/>
<outlet property="finalImage" destination="gzv-K4-5OL" id="YIw-kM-Ld6"/>
<outlet property="imageGestureRecognizer" destination="aDF-hC-ddO" id="2jh-Rr-eKk"/>
<outlet property="imageSubView" destination="GmN-7v-uuO" id="k9r-Xc-csv"/>
<outlet property="imdmIcon" destination="LPj-VT-0fH" id="yYh-pv-EJs"/>
<outlet property="imdmLabel" destination="44j-me-Iqi" id="m5R-Dm-V8g"/>
<outlet property="messageImageView" destination="yMW-cT-bpU" id="MNr-F2-abQ"/>
<outlet property="openRecognizer" destination="NYA-II-xYn" id="pVM-vD-4Rg"/>
<outlet property="playButton" destination="cvc-tl-Pcf" id="eKJ-2T-LUl"/>
<outlet property="resendRecognizer" destination="5ZI-Ip-lGl" id="G2r-On-6mV"/>
<outlet property="statusInProgressSpinner" destination="Eab-ND-ix3" id="UuC-eY-MSf"/>
<outlet property="totalView" destination="8I3-n2-0kS" id="aa8-j9-saW"/>
@ -32,15 +36,15 @@
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" misplaced="YES" id="UGz-WT-BUv">
<rect key="frame" x="0.0" y="0.0" width="334" height="161"/>
<rect key="frame" x="0.0" y="0.0" width="377" height="301"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
<subviews>
<view clipsSubviews="YES" contentMode="scaleToFill" id="Y7i-Gm-AdY" userLabel="innerView">
<rect key="frame" x="6" y="5" width="322" height="151"/>
<rect key="frame" x="6" y="5" width="365" height="291"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" alpha="0.20000000298023224" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="color_A.png" id="U2P-5n-gg8" userLabel="backgroundColorImage">
<rect key="frame" x="0.0" y="0.0" width="322" height="151"/>
<rect key="frame" x="0.0" y="0.0" width="365" height="291"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES" flexibleMaxY="YES"/>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" image="avatar.png" id="hD2-19-7IH" userLabel="avatarImage" customClass="UIRoundedImageView">
@ -52,35 +56,40 @@
</accessibility>
</imageView>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="11:35 John Doe" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="JyR-RQ-uwF" userLabel="contactDateLabel">
<rect key="frame" x="55" y="8" width="246" height="14"/>
<rect key="frame" x="55" y="8" width="286" height="14"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" label="Contact name"/>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<color key="textColor" red="0.98766469955444336" green="0.27512490749359131" blue="0.029739789664745331" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" id="8I3-n2-0kS" userLabel="view">
<rect key="frame" x="55" y="24" width="259" height="100"/>
<view contentMode="scaleToFill" misplaced="YES" id="8I3-n2-0kS" userLabel="view">
<rect key="frame" x="39" y="55" width="298" height="190"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES" flexibleMaxY="YES"/>
<subviews>
<imageView contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="linphone_logo.png" id="yMW-cT-bpU" userLabel="image" customClass="UILoadingImageView">
<rect key="frame" x="0.0" y="0.0" width="259" height="68"/>
<rect key="frame" x="0.0" y="0.0" width="298" height="122"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES" flexibleMaxY="YES"/>
<gestureRecognizers/>
<connections>
<outletCollection property="gestureRecognizers" destination="aDF-hC-ddO" appends="YES" id="7kE-0j-s1g"/>
</connections>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="Label" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Dho-UV-6Ev" userLabel="fileName">
<rect key="frame" x="0.0" y="0.0" width="200" height="50"/>
<autoresizingMask key="autoresizingMask"/>
<color key="backgroundColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" id="GmN-7v-uuO" userLabel="imageSubView">
<rect key="frame" x="0.0" y="68" width="258" height="32"/>
<rect key="frame" x="0.0" y="128" width="297" height="62"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES"/>
<subviews>
<progressView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" progress="0.5" id="USm-wC-GvG" userLabel="transferProgress">
<rect key="frame" x="10" y="0.0" width="238" height="2"/>
<rect key="frame" x="10" y="29" width="277" height="2"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
</progressView>
<button opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="N75-gL-R6t" userLabel="downloadButton" customClass="UIRoundBorderedButton">
<rect key="frame" x="66" y="4" width="115" height="27"/>
<rect key="frame" x="84" y="33" width="115" height="27"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
<accessibility key="accessibilityConfiguration" label="Download"/>
<state key="normal" title="DOWNLOAD" backgroundImage="color_G.png">
@ -92,7 +101,7 @@
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="6dl-Nz-rdv" userLabel="cancelButton" customClass="UIRoundBorderedButton">
<rect key="frame" x="66" y="4" width="115" height="27"/>
<rect key="frame" x="84" y="33" width="115" height="27"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
<accessibility key="accessibilityConfiguration" label="Cancel"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
@ -108,28 +117,56 @@
</view>
</subviews>
</view>
<view contentMode="scaleToFill" misplaced="YES" id="VYJ-RC-Jmg">
<rect key="frame" x="8" y="55" width="349" height="213"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" id="gzv-K4-5OL" userLabel="finalImage">
<rect key="frame" x="0.0" y="0.0" width="349" height="213"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<gestureRecognizers/>
</imageView>
<button opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="cvc-tl-Pcf" userLabel="playButton" customClass="UIRoundBorderedButton">
<rect key="frame" x="125" y="93" width="50" height="25"/>
<autoresizingMask key="autoresizingMask"/>
<accessibility key="accessibilityConfiguration" label="Cancel"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<state key="normal" title="PLAY" backgroundImage="color_I.png">
<color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</state>
<state key="highlighted" backgroundImage="color_M.png"/>
<connections>
<action selector="onPlayClick:" destination="-1" eventType="touchUpInside" id="B4y-PJ-4tO"/>
</connections>
</button>
</subviews>
<connections>
<outletCollection property="gestureRecognizers" destination="aDF-hC-ddO" appends="YES" id="lKJ-ra-dwR"/>
<outletCollection property="gestureRecognizers" destination="NYA-II-xYn" appends="YES" id="fK6-ld-zOX"/>
</connections>
</view>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="color_A.png" id="6dA-3U-OPW" userLabel="bottomBarColor">
<rect key="frame" x="0.0" y="150" width="322" height="1"/>
<rect key="frame" x="0.0" y="290" width="365" height="1"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
</imageView>
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" animating="YES" style="gray" id="Eab-ND-ix3" userLabel="statusInprogressSpinner">
<rect key="frame" x="302" y="0.0" width="20" height="20"/>
<rect key="frame" x="345" y="0.0" width="20" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
</activityIndicatorView>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" image="chat_unsecure.png" id="IST-5o-DCu" userLabel="LIMEKO">
<rect key="frame" x="308" y="6" width="8" height="12"/>
<rect key="frame" x="351" y="6" width="8" height="12"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" label="Delivery failed"/>
</imageView>
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="Delivered" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="44j-me-Iqi" userLabel="imdmLabel">
<rect key="frame" x="240" y="134" width="64" height="20"/>
<rect key="frame" x="283" y="274" width="64" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="11"/>
<color key="textColor" red="1" green="0.36862745099999999" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" image="valid_default.png" id="LPj-VT-0fH" userLabel="imdmIcon">
<rect key="frame" x="306" y="136" width="13" height="13"/>
<rect key="frame" x="349" y="276" width="13" height="13"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
<accessibility key="accessibilityConfiguration" label="Delivery failed"/>
</imageView>
@ -142,16 +179,21 @@
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="-95" y="176.5"/>
<point key="canvasLocation" x="-73.5" y="246.5"/>
</view>
<tapGestureRecognizer id="5ZI-Ip-lGl" userLabel="resendClick">
<connections>
<action selector="onResendClick:" destination="-1" id="XOF-AF-Hes"/>
</connections>
</tapGestureRecognizer>
<tapGestureRecognizer id="aDF-hC-ddO" userLabel="imageClick">
<connections>
<action selector="onImageClick:" destination="-1" id="feN-LT-89b"/>
</connections>
</tapGestureRecognizer>
<tapGestureRecognizer id="5ZI-Ip-lGl" userLabel="resendClick">
<tapGestureRecognizer id="NYA-II-xYn" userLabel="openClick">
<connections>
<action selector="onResendClick:" destination="-1" id="XOF-AF-Hes"/>
<action selector="onOpenClick:" destination="-1" id="XaJ-Or-uQQ"/>
</connections>
</tapGestureRecognizer>
</objects>
@ -162,7 +204,7 @@
<image name="color_G.png" width="2" height="2"/>
<image name="color_I.png" width="2" height="2"/>
<image name="color_M.png" width="2" height="2"/>
<image name="linphone_logo.png" width="26" height="22"/>
<image name="linphone_logo.png" width="26" height="26"/>
<image name="valid_default.png" width="28" height="19"/>
</resources>
</document>

View file

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13196" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13174"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>

View file

@ -1,8 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="9531" systemVersion="15C50" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none">
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="UIChatCell">
@ -11,6 +15,7 @@
<outlet property="avatarImage" destination="19" id="30"/>
<outlet property="chatContentLabel" destination="21" id="24"/>
<outlet property="chatLatestTimeLabel" destination="r7A-Mk-LQX" id="J0r-yr-YLk"/>
<outlet property="imdmIcon" destination="aa2-Kl-c1H" id="rnr-XY-r9K"/>
<outlet property="unreadCountLabel" destination="ZXq-Do-7Ua" id="anx-Na-sYz"/>
<outlet property="unreadCountView" destination="7DE-KJ-9Q3" id="hkt-rA-EKa"/>
</connections>
@ -29,7 +34,7 @@
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" label=""/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="25"/>
<color key="textColor" red="1" green="0.36862745099999999" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="textColor" red="0.98766469955444336" green="0.27512490749359131" blue="0.029739789664745331" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="2" contentMode="left" text="John Doe" lineBreakMode="tailTruncation" minimumFontSize="10" adjustsFontSizeToFit="NO" id="20" userLabel="addressLabel">
@ -37,15 +42,7 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" label="Contact name"/>
<fontDescription key="fontDescription" type="system" pointSize="25"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="2" contentMode="left" text="This is a placeholder message" lineBreakMode="tailTruncation" numberOfLines="3" adjustsFontSizeToFit="NO" id="21" userLabel="chatContentLabel">
<rect key="frame" x="59" y="31" width="316" height="29"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES"/>
<accessibility key="accessibilityConfiguration" label="Message"/>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<view hidden="YES" autoresizesSubviews="NO" userInteractionEnabled="NO" tag="7" contentMode="scaleToFill" id="7DE-KJ-9Q3" userLabel="unreadCountView" customClass="UIBouncingView">
@ -64,12 +61,27 @@
<bool key="isElement" value="NO"/>
</accessibility>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
</view>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" image="chat_read.png" id="aa2-Kl-c1H" userLabel="imdmIcon">
<rect key="frame" x="59" y="39" width="13" height="13"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" label="Delivery failed">
<accessibilityTraits key="traits" button="YES" image="YES"/>
</accessibility>
</imageView>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="2" contentMode="left" misplaced="YES" text="This is a placeholder message" lineBreakMode="tailTruncation" numberOfLines="3" adjustsFontSizeToFit="NO" id="21" userLabel="chatContentLabel">
<rect key="frame" x="80" y="31" width="316" height="29"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES"/>
<accessibility key="accessibilityConfiguration" label="Message"/>
<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>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
@ -77,7 +89,8 @@
</view>
</objects>
<resources>
<image name="avatar.png" width="255" height="255"/>
<image name="history_chat_indicator.png" width="13" height="13"/>
<image name="avatar.png" width="259" height="259"/>
<image name="chat_read.png" width="24" height="24"/>
<image name="history_chat_indicator.png" width="14" height="13"/>
</resources>
</document>

View file

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14109" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" 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="14088"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@ -25,18 +25,18 @@
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" fixedFrame="YES" insetsLayoutMarginsFromSafeArea="NO" image="avatar.png" translatesAutoresizingMaskIntoConstraints="NO" id="Z2U-vm-azg" userLabel="avatarImage" customClass="UIRoundedImageView">
<rect key="frame" x="0.0" y="5" width="44" height="29"/>
<rect key="frame" x="0.0" y="5" width="44" height="28"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" insetsLayoutMarginsFromSafeArea="NO" text="John Doe" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="f3q-pd-EF5" userLabel="displayNameLabel">
<rect key="frame" x="43" y="11" width="185" height="17"/>
<rect key="frame" x="43" y="11" width="185" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" fixedFrame="YES" insetsLayoutMarginsFromSafeArea="NO" text="Today - 18h42" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hEV-7I-B0v" userLabel="contactDateLabel">
<rect key="frame" x="200" y="13" width="159" height="13"/>
<rect key="frame" x="200" y="13" width="159" height="12"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" heightSizable="YES"/>
<accessibility key="accessibilityConfiguration" label="Contact name"/>
<fontDescription key="fontDescription" type="system" pointSize="12"/>

View file

@ -29,10 +29,14 @@
@property(nonatomic, strong) IBOutlet UILoadingImageView *messageImageView;
@property(nonatomic, strong) IBOutlet UIButton *downloadButton;
@property (weak, nonatomic) IBOutlet UILabel *fileName;
@property(nonatomic, strong) IBOutlet UIButton *playButton;
@property(weak, nonatomic) IBOutlet UIProgressView *fileTransferProgress;
@property(weak, nonatomic) IBOutlet UIButton *cancelButton;
@property(weak, nonatomic) IBOutlet UIView *imageSubView;
@property (strong, nonatomic) IBOutlet UITapGestureRecognizer *openRecognizer;
@property(weak, nonatomic) IBOutlet UIView *totalView;
@property (weak, nonatomic) IBOutlet UIImageView *finalImage;
@property(strong, nonatomic) IBOutlet UITapGestureRecognizer *imageGestureRecognizer;
- (void)setEvent:(LinphoneEventLog *)event;
@ -42,5 +46,10 @@
- (IBAction)onImageClick:(id)event;
- (IBAction)onCancelClick:(id)sender;
- (IBAction)onResendClick:(id)event;
- (IBAction)onPlayClick:(id)sender;
- (IBAction)onOpenClick:(id)event;
@end

View file

@ -23,9 +23,16 @@
#import <AssetsLibrary/ALAsset.h>
#import <AssetsLibrary/ALAssetRepresentation.h>
#import <AVFoundation/AVFoundation.h>
#import <AudioToolbox/AudioToolbox.h>
#import <AVKit/AVKit.h>
@implementation UIChatBubblePhotoCell {
FileTransferDelegate *_ftd;
CGSize imageSize, bubbleSize, videoDefaultSize;
int actualAvailableWidth;
ChatConversationTableView *chatTableView;
//CGImageRef displayedImage;
}
#pragma mark - Lifecycle Functions
@ -44,8 +51,11 @@
break;
}
}
[self setFrame:CGRectMake(0, 0, sub.frame.size.width, sub.frame.size.height)];
[self setFrame:CGRectMake(0, 0, 5, 100)];
[self addSubview:sub];
chatTableView = VIEW(ChatConversationView).tableController;
actualAvailableWidth = chatTableView.tableView.frame.size.width;
videoDefaultSize = CGSizeMake(320, 240);
}
return self;
}
@ -61,7 +71,10 @@
- (void)setChatMessage:(LinphoneChatMessage *)amessage {
_imageGestureRecognizer.enabled = NO;
_openRecognizer.enabled = NO;
_messageImageView.image = nil;
_finalImage.image = nil;
_finalImage.hidden = TRUE;
_fileTransferProgress.progress = 0;
[self disconnectFromFileDelegate];
@ -85,15 +98,62 @@
[super setChatMessage:amessage];
}
- (void) loadImageAsset:(ALAsset*) asset thumb:(UIImage *)thumb image:(UIImage *)image {
dispatch_async(dispatch_get_main_queue(), ^{
[_finalImage setImage:image];
[_messageImageView setImage:thumb];
[_messageImageView setFullImageUrl:asset];
[_messageImageView stopLoading];
_messageImageView.hidden = YES;
_imageGestureRecognizer.enabled = YES;
_finalImage.hidden = NO;
});
}
- (void) loadAsset:(ALAsset*) asset {
UIImage *image = [[UIImage alloc] initWithCGImage:[asset thumbnail]];
dispatch_async(dispatch_get_main_queue(), ^{
[_messageImageView setImage:image];
[_messageImageView setFullImageUrl:asset];
[_messageImageView stopLoading];
_messageImageView.hidden = NO;
_imageGestureRecognizer.enabled = YES;
});
UIImage *thumb = [[UIImage alloc] initWithCGImage:[asset thumbnail]];
ALAssetRepresentation *representation = [asset defaultRepresentation];
imageSize = [UIChatBubbleTextCell getMediaMessageSizefromOriginalSize:[representation dimensions] withWidth:chatTableView.tableView.frame.size.width];
CGImageRef tmpImg = [self cropImageFromRepresentation:representation];
UIImage *image = [[UIImage alloc] initWithCGImage:tmpImg];
[self loadImageAsset:asset thumb:thumb image:image];
}
- (void) loadVideoAsset: (AVAsset *) asset {
// Calculate a time for the snapshot - I'm using the half way mark.
CMTime duration = [asset duration];
CMTime snapshot = CMTimeMake(duration.value / 2, duration.timescale);
// Create a generator and copy image at the time.
// I'm not capturing the actual time or an error.
AVAssetImageGenerator *generator =
[AVAssetImageGenerator assetImageGeneratorWithAsset:asset];
CGImageRef imageRef = [generator copyCGImageAtTime:snapshot
actualTime:nil
error:nil];
UIImage *thumb = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
UIGraphicsBeginImageContext(videoDefaultSize);
[thumb drawInRect:CGRectMake(0, 0, videoDefaultSize.width, videoDefaultSize.height)];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self loadImageAsset:nil thumb:thumb image:image];
// put the play button in the top
CGRect newFrame = _playButton.frame;
newFrame.origin.x = _finalImage.frame.origin.x/2;
newFrame.origin.y = _finalImage.frame.origin.y/2;
_playButton.frame = newFrame;
}
- (void) loadFileAsset {
dispatch_async(dispatch_get_main_queue(), ^{
_fileName.hidden = NO;
_imageGestureRecognizer.enabled = NO;
_openRecognizer.enabled = YES;
});
}
- (void)update {
@ -107,77 +167,107 @@
BOOL is_external =
(url && (strstr(url, "http") == url)) || linphone_chat_message_get_file_transfer_information(self.message);
NSString *localImage = [LinphoneManager getMessageAppDataForKey:@"localimage" inMessage:self.message];
NSString *localVideo = [LinphoneManager getMessageAppDataForKey:@"localvideo" inMessage:self.message];
NSString *localFile = [LinphoneManager getMessageAppDataForKey:@"localfile" inMessage:self.message];
BOOL fullScreenImage = NO;
assert(is_external || localImage);
if (localImage) {
// image is being saved on device - just wait for it
if ([localImage isEqualToString:@"saving..."]) {
_cancelButton.hidden = _fileTransferProgress.hidden = _downloadButton.hidden = YES;
fullScreenImage = YES;
} else {
// we did not load the image yet, so start doing so
if (_messageImageView.image == nil) {
NSURL *imageUrl = [NSURL URLWithString:localImage];
[_messageImageView startLoading];
__block LinphoneChatMessage *achat = self.message;
[LinphoneManager.instance.photoLibrary assetForURL:imageUrl
resultBlock:^(ALAsset *asset) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, (unsigned long)NULL),
^(void) {
if (achat != self.message) // Avoid glitch and scrolling
return;
if (asset)
[self loadAsset:asset];
else {
[LinphoneManager.instance.photoLibrary
enumerateGroupsWithTypes:ALAssetsGroupAll
usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
[group enumerateAssetsWithOptions:NSEnumerationReverse
usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
if([result.defaultRepresentation.url isEqual:imageUrl]) {
[self loadAsset:result];
*stop = YES;
}
}];
}
failureBlock:^(NSError *error) {
LOGE(@"Error: Cannot load asset from photo stream - %@", [error localizedDescription]);
}];
}
});
}
failureBlock:^(NSError *error) {
LOGE(@"Can't read image");
}];
}
// we are uploading the image
if (_ftd.message != nil) {
_cancelButton.hidden = NO;
_fileTransferProgress.hidden = NO;
_downloadButton.hidden = YES;
} else {
_cancelButton.hidden = _fileTransferProgress.hidden = _downloadButton.hidden = YES;
fullScreenImage = YES;
}
}
// we must download the image: either it has already started (show cancel button) or not yet (show download
// button)
} else {
_messageImageView.hidden = _cancelButton.hidden = (_ftd.message == nil);
_downloadButton.hidden = !_cancelButton.hidden;
_fileTransferProgress.hidden = NO;
}
assert(is_external || localImage || localVideo || localFile);
if (!(localImage || localVideo || localFile)) {
_playButton.hidden = YES;
_fileName.hidden = YES;
_messageImageView.hidden = _cancelButton.hidden = (_ftd.message == nil);
_downloadButton.hidden = !_cancelButton.hidden;
_fileTransferProgress.hidden = NO;
} else {
// file is being saved on device - just wait for it
if ([localImage isEqualToString:@"saving..."] || [localVideo isEqualToString:@"saving..."] || [localFile isEqualToString:@"saving..."]) {
_cancelButton.hidden = _fileTransferProgress.hidden = _downloadButton.hidden = _playButton.hidden = _fileName.hidden = YES;
fullScreenImage = YES;
} else {
if (localImage) {
// we did not load the image yet, so start doing so
if (_messageImageView.image == nil) {
NSURL *imageUrl = [NSURL URLWithString:localImage];
[_messageImageView startLoading];
__block LinphoneChatMessage *achat = self.message;
[LinphoneManager.instance.photoLibrary assetForURL:imageUrl resultBlock:^(ALAsset *asset) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, (unsigned long)NULL), ^(void) {
if (achat != self.message) // Avoid glitch and scrolling
return;
if (asset) {
[self loadAsset:asset];
}
else {
[LinphoneManager.instance.photoLibrary
enumerateGroupsWithTypes:ALAssetsGroupAll
usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
[group enumerateAssetsWithOptions:NSEnumerationReverse
usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
if([result.defaultRepresentation.url isEqual:imageUrl]) {
[self loadAsset:result];
*stop = YES;
}
}];
}
failureBlock:^(NSError *error) {
LOGE(@"Error: Cannot load asset from photo stream - %@", [error localizedDescription]);
}];
}
});
} failureBlock:^(NSError *error) {
LOGE(@"Can't read image");
}];
}
} else if (localVideo) {
if (_messageImageView.image == nil) {
[_messageImageView startLoading];
// read video from Documents
NSString *filePath = [LinphoneManager documentFile:localVideo];
NSURL *url = [NSURL fileURLWithPath:filePath];
AVAsset *asset = [AVAsset assetWithURL:url];
if (asset)
[self loadVideoAsset:asset];
}
} else if (localFile) {
NSString *text = [NSString stringWithFormat:@"📎 %@",localFile];
_fileName.text = text;
[self loadFileAsset];
}
// we are uploading the image
if (_ftd.message != nil) {
_cancelButton.hidden = NO;
_fileTransferProgress.hidden = NO;
_downloadButton.hidden = YES;
_playButton.hidden = YES;
_fileName.hidden = YES;
} else {
_cancelButton.hidden = _fileTransferProgress.hidden = _downloadButton.hidden = YES;
fullScreenImage = YES;
_playButton.hidden = localVideo ? NO : YES;
_fileName.hidden = localFile ? NO : YES;
}
}
}
// resize image so that it take the full bubble space available
CGRect newFrame = _totalView.frame;
newFrame.origin.x = newFrame.origin.y = 0;
if (!fullScreenImage) {
newFrame.size.height -= _imageSubView.frame.size.height;
}
}
_messageImageView.frame = newFrame;
}
- (void)fileErrorBlock {
DTActionSheet *sheet = [[DTActionSheet alloc] initWithTitle:NSLocalizedString(@"Can't open this file", nil)];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[sheet addCancelButtonWithTitle:NSLocalizedString(@"OK", nil) block:nil];
dispatch_async(dispatch_get_main_queue(), ^{
[sheet showInView:PhoneMainView.instance.view];
});
});
}
- (IBAction)onDownloadClick:(id)event {
[_ftd cancel];
_ftd = [[FileTransferDelegate alloc] init];
@ -185,8 +275,41 @@
[_ftd download:self.message];
_cancelButton.hidden = NO;
_downloadButton.hidden = YES;
_playButton.hidden = YES;
_fileName.hidden = YES;
}
- (IBAction)onPlayClick:(id)sender {
NSString *localVideo = [LinphoneManager getMessageAppDataForKey:@"localvideo" inMessage:self.message];
NSString *filePath = [LinphoneManager documentFile:localVideo];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:filePath]) {
// create a player view controller
AVPlayer *player = [AVPlayer playerWithURL:[[NSURL alloc] initFileURLWithPath:filePath]];
AVPlayerViewController *controller = [[AVPlayerViewController alloc] init];
[PhoneMainView.instance presentViewController:controller animated:YES completion:nil];
controller.player = player;
[player play];
} else {
[self fileErrorBlock];
}
}
- (IBAction)onOpenClick:(id)event {
NSString *localFile = [LinphoneManager getMessageAppDataForKey:@"localfile" inMessage:self.message];
NSString *filePath = [LinphoneManager documentFile:localFile];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:filePath]) {
ChatConversationView *view = VIEW(ChatConversationView);
[view openResults:filePath];
} else {
[self fileErrorBlock];
}
}
- (IBAction)onCancelClick:(id)sender {
FileTransferDelegate *tmp = _ftd;
[self disconnectFromFileDelegate];
@ -200,7 +323,7 @@
[self onDownloadClick:event];
} else if (_cancelButton.hidden == NO) {
[self onCancelClick:event];
} else {
} else {
[super onResend];
}
}
@ -276,4 +399,70 @@
}
}
+ (CGImageRef)resizeCGImage:(CGImageRef)image toWidth:(int)width andHeight:(int)height {
// create context, keeping original image properties
CGColorSpaceRef colorspace = CGImageGetColorSpace(image);
CGContextRef context = CGBitmapContextCreate(NULL, width, height,
CGImageGetBitsPerComponent(image),
CGImageGetBytesPerRow(image),
colorspace,
CGImageGetAlphaInfo(image));
CGColorSpaceRelease(colorspace);
if(context == NULL)
return nil;
// draw image to context (resizing it)
CGContextDrawImage(context, CGRectMake(0, 0, width, height), image);
// extract resulting image from context
CGImageRef imgRef = CGBitmapContextCreateImage(context);
CGContextRelease(context);
return imgRef;
}
- (void)layoutSubviews {
[super layoutSubviews];
//if (!isAssetLoaded) return;
UITableView *tableView = VIEW(ChatConversationView).tableController.tableView;
BOOL is_outgoing = linphone_chat_message_is_outgoing(super.message);
CGRect bubbleFrame = super.bubbleView.frame;
int origin_x;
bubbleSize = [UIChatBubbleTextCell ViewSizeForMessage:[self message] withWidth:chatTableView.tableView.frame.size.width];
bubbleFrame.size = bubbleSize;
if (tableView.isEditing) {
origin_x = 0;
} else {
origin_x = (is_outgoing ? self.frame.size.width - bubbleFrame.size.width : 0);
}
bubbleFrame.origin.x = origin_x;
super.bubbleView.frame = bubbleFrame;
}
- (CGImageRef)cropImageFromRepresentation:(ALAssetRepresentation*)rep {
CGImageRef newImage = [rep fullResolutionImage];
CGSize originalSize = [rep dimensions];
float originalAspectRatio = originalSize.width / originalSize.height;
// We resize in width and crop in height
if (originalSize.width > imageSize.width) {
int height = imageSize.width / originalAspectRatio;
newImage = [self.class resizeCGImage:newImage toWidth:imageSize.width andHeight:height];
originalSize.height = height;
}
CGRect cropRect = CGRectMake(0, 0, imageSize.width, imageSize.height);
if (imageSize.height < originalSize.height) cropRect.origin.y = (originalSize.height - imageSize.height)/2;
newImage = CGImageCreateWithImageInRect(newImage, cropRect);
LOGD([NSString stringWithFormat:@"Image size : width = %g, height = %g", imageSize.width, imageSize.height]);
LOGD([NSString stringWithFormat:@"Bubble size : width = %g, height = %g", super.bubbleView.frame.size.width, super.bubbleView.frame.size.height]);
return newImage;
}
@end

View file

@ -41,6 +41,7 @@
@property(weak, nonatomic) IBOutlet UILabel *imdmLabel;
+ (CGSize)ViewSizeForMessage:(LinphoneChatMessage *)chat withWidth:(int)width;
+ (CGSize)getMediaMessageSizefromOriginalSize:(CGSize)originalSize withWidth:(int)width;
- (void)setEvent:(LinphoneEventLog *)event;
- (void)setChatMessage:(LinphoneChatMessage *)message;

View file

@ -232,19 +232,27 @@
if (linphone_chat_message_get_file_transfer_information(_message) != NULL) {
NSString *localImage = [LinphoneManager getMessageAppDataForKey:@"localimage" inMessage:_message];
NSNumber *uploadQuality =[LinphoneManager getMessageAppDataForKey:@"uploadQuality" inMessage:_message];
NSString *localVideo = [LinphoneManager getMessageAppDataForKey:@"localvideo" inMessage:_message];
NSString *localFile = [LinphoneManager getMessageAppDataForKey:@"localfile" inMessage:_message];
NSString *fileName = localVideo ? localVideo : localFile;
NSURL *imageUrl = [NSURL URLWithString:localImage];
[self onDelete];
[LinphoneManager.instance.photoLibrary assetForURL:imageUrl
resultBlock:^(ALAsset *asset) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, (unsigned long)NULL),
^(void) {
UIImage *image = [[UIImage alloc] initWithCGImage:[[asset defaultRepresentation] fullResolutionImage]];
[_chatRoomDelegate startImageUpload:image url:imageUrl withQuality:(uploadQuality ? [uploadQuality floatValue] : 0.9)];
});
}
failureBlock:^(NSError *error) {
LOGE(@"Can't read image");
}];
if(localImage){
[LinphoneManager.instance.photoLibrary assetForURL:imageUrl
resultBlock:^(ALAsset *asset) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, (unsigned long)NULL),
^(void) {
UIImage *image = [[UIImage alloc] initWithCGImage:[[asset defaultRepresentation] fullResolutionImage]];
[_chatRoomDelegate startImageUpload:image url:imageUrl withQuality:(uploadQuality ? [uploadQuality floatValue] : 0.9)];
});
}
failureBlock:^(NSError *error) {
LOGE(@"Can't read image");
}];
} else if(fileName) {
NSString *filePath = [LinphoneManager documentFile:fileName];
[_chatRoomDelegate startFileUpload:[NSData dataWithContentsOfFile:filePath] withUrl:[NSURL URLWithString:filePath]];
}
} else {
[self onDelete];
double delayInSeconds = 0.4;
@ -306,12 +314,11 @@ static const CGFloat CELL_MIN_HEIGHT = 60.0f;
static const CGFloat CELL_MIN_WIDTH = 190.0f;
static const CGFloat CELL_MESSAGE_X_MARGIN = 78 + 10.0f;
static const CGFloat CELL_MESSAGE_Y_MARGIN = 52; // 44;
static const CGFloat CELL_IMAGE_HEIGHT = 100.0f;
static const CGFloat CELL_IMAGE_WIDTH = 100.0f;
+ (CGSize)ViewHeightForMessage:(LinphoneChatMessage *)chat withWidth:(int)width {
NSString *messageText = [UIChatBubbleTextCell TextMessageForChat:chat];
static UIFont *messageFont = nil;
if (!messageFont) {
UIChatBubbleTextCell *cell =
[[UIChatBubbleTextCell alloc] initWithIdentifier:NSStringFromClass(UIChatBubbleTextCell.class)];
@ -328,8 +335,39 @@ static const CGFloat CELL_IMAGE_WIDTH = 100.0f;
font:messageFont];
} else {
NSString *localImage = [LinphoneManager getMessageAppDataForKey:@"localimage" inMessage:chat];
size = (localImage != nil) ? CGSizeMake(CELL_IMAGE_WIDTH, CELL_IMAGE_HEIGHT) : CGSizeMake(50, 50);
NSString *localFile = [LinphoneManager getMessageAppDataForKey:@"localfile" inMessage:chat];
NSString *localVideo = [LinphoneManager getMessageAppDataForKey:@"localvideo" inMessage:chat];
if(localFile) {
CGSize fileSize = CGSizeMake(200, 80);
size = [self getMediaMessageSizefromOriginalSize:fileSize withWidth:width];
} else if (localVideo) {
CGSize videoSize = CGSizeMake(320, 240);
size = [self getMediaMessageSizefromOriginalSize:videoSize withWidth:width];
size.height += CELL_MESSAGE_X_MARGIN;
} else {
NSURL *imageUrl = [NSURL URLWithString:localImage];
__block CGSize originalImageSize = CGSizeMake(0, 0);
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
[LinphoneManager.instance.photoLibrary assetForURL:imageUrl
resultBlock:^(ALAsset *asset) {
originalImageSize = [[asset defaultRepresentation] dimensions];
dispatch_semaphore_signal(sema);
}
failureBlock:^(NSError *error) {
LOGE(@"Can't read image");
dispatch_semaphore_signal(sema);
}];
});
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
size = [self getMediaMessageSizefromOriginalSize:originalImageSize withWidth:width];
//This fixes the image being too small. I think the issue comes form the fact that the display is retina. This should probably be changed in the future.
size.height += CELL_MESSAGE_X_MARGIN;
}
}
size.width = MAX(size.width + CELL_MESSAGE_X_MARGIN, CELL_MIN_WIDTH);
size.height = MAX(size.height + CELL_MESSAGE_Y_MARGIN, CELL_MIN_HEIGHT);
return size;
@ -375,4 +413,22 @@ static const CGFloat CELL_IMAGE_WIDTH = 100.0f;
}
}
+ (CGSize)getMediaMessageSizefromOriginalSize:(CGSize)originalSize withWidth:(int)width {
CGSize mediaSize = CGSizeMake(0, 0);
int availableWidth = width - CELL_MESSAGE_X_MARGIN;
if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) {
availableWidth = availableWidth /3;
}
int newHeight = originalSize.height;
float originalAspectRatio = originalSize.width / originalSize.height;
// We resize in width and crop in height
if (originalSize.width > availableWidth) {
newHeight = availableWidth / originalAspectRatio;
}
mediaSize.height = MIN(newHeight, availableWidth);
mediaSize.width = MIN(availableWidth, originalSize.width);
return mediaSize;
}
@end

View file

@ -35,6 +35,7 @@
@property(weak, nonatomic) IBOutlet UILabel *chatLatestTimeLabel;
@property(weak, nonatomic) IBOutlet UIBouncingView *unreadCountView;
@property(weak, nonatomic) IBOutlet UILabel *unreadCountLabel;
@property (weak, nonatomic) IBOutlet UIImageView *imdmIcon;
- (id)initWithIdentifier:(NSString*)identifier;

View file

@ -85,17 +85,66 @@
LinphoneChatMessage *last_msg = linphone_chat_room_get_last_message_in_history(chatRoom);
if (last_msg) {
NSString *text = [[FastAddressBook displayNameForAddress:linphone_chat_message_get_from_address(last_msg)]
stringByAppendingFormat:@" : %@", [UIChatBubbleTextCell TextMessageForChat:last_msg]];
// shorten long messages
if ([text length] > 50)
text = [[text substringToIndex:50] stringByAppendingString:@"[...]"];
BOOL imdnInSnap = FALSE;
if (imdnInSnap) {
BOOL outgoing = linphone_chat_message_is_outgoing(last_msg);
NSString *text = [UIChatBubbleTextCell TextMessageForChat:last_msg];
if (outgoing) {
// shorten long messages
if ([text length] > 50)
text = [[text substringToIndex:50] stringByAppendingString:@"[...]"];
_chatContentLabel.attributedText = nil;
_chatContentLabel.text = text;
}
else {
NSString *name = [FastAddressBook displayNameForAddress:linphone_chat_message_get_from_address(last_msg)];
if ([name length] > 25) {
name = [[name substringToIndex:25] stringByAppendingString:@"[...]"];
}
CGFloat fontSize = _chatContentLabel.font.pointSize;
UIFont *boldFont = [UIFont boldSystemFontOfSize:fontSize];
NSMutableAttributedString *boldText = [[NSMutableAttributedString alloc] initWithString:name attributes:@{ NSFontAttributeName : boldFont }];
text = [@" : " stringByAppendingString:text];
NSString *fullText = [name stringByAppendingString:text];
if ([fullText length] > 50) {
text = [[text substringToIndex: (50 - [name length])] stringByAppendingString:@"[...]"];
}
[boldText appendAttributedString:[[NSAttributedString alloc] initWithString:text]];
_chatContentLabel.text = nil;
_chatContentLabel.attributedText = boldText;
}
_chatContentLabel.text = text;
LinphoneChatMessageState state = linphone_chat_message_get_state(last_msg);
if (outgoing && (state == LinphoneChatMessageStateDeliveredToUser || state == LinphoneChatMessageStateDisplayed || state == LinphoneChatMessageStateNotDelivered || state == LinphoneChatMessageStateFileTransferError)) {
[self displayImdmStatus:state];
CGRect newFrame = _chatContentLabel.frame;
newFrame.origin.x = 80;
_chatContentLabel.frame = newFrame;
} else {
// We displace the message 20 pixels to the left
[_imdmIcon setHidden:TRUE];
CGRect newFrame = _chatContentLabel.frame;
newFrame.origin.x = 60;
_chatContentLabel.frame = newFrame;
}
} else {
NSString *text = [[FastAddressBook displayNameForAddress:linphone_chat_message_get_from_address(last_msg)]
stringByAppendingFormat:@" : %@", [UIChatBubbleTextCell TextMessageForChat:last_msg]];
// shorten long messages
if ([text length] > 50)
text = [[text substringToIndex:50] stringByAppendingString:@"[...]"];
[_imdmIcon setHidden:TRUE];
CGRect newFrame = _chatContentLabel.frame;
newFrame.origin.x = 60;
_chatContentLabel.frame = newFrame;
_chatContentLabel.text = text;
}
linphone_chat_message_unref(last_msg);
} else
_chatContentLabel.text = nil;
[self updateUnreadBadge];
}
@ -142,4 +191,20 @@
}
}
- (void)displayImdmStatus:(LinphoneChatMessageState)state {
if (state == LinphoneChatMessageStateDeliveredToUser) {
[_imdmIcon setImage:[UIImage imageNamed:@"chat_delivered"]];
[_imdmIcon setHidden:FALSE];
} else if (state == LinphoneChatMessageStateDisplayed) {
[_imdmIcon setImage:[UIImage imageNamed:@"chat_read"]];
[_imdmIcon setHidden:FALSE];
} else if (state == LinphoneChatMessageStateNotDelivered || state == LinphoneChatMessageStateFileTransferError) {
[_imdmIcon setImage:[UIImage imageNamed:@"chat_error"]];
[_imdmIcon setHidden:FALSE];
} else {
[_imdmIcon setHidden:TRUE];
}
}
@end

View file

@ -82,6 +82,7 @@
}
- (void)stopLoading {
[waitIndicatorView stopAnimating];
}

View file

@ -762,6 +762,8 @@ void update_hash_cbs(LinphoneAccountCreator *creator, LinphoneAccountCreatorStat
if (!linphone_chat_message_is_outgoing(msg)) {
[LinphoneManager setValueInMessageAppData:nil forKey:@"localimage" inMessage:msg];
[LinphoneManager setValueInMessageAppData:nil forKey:@"uploadQuality" inMessage:msg];
[LinphoneManager setValueInMessageAppData:nil forKey:@"localvideo" inMessage:msg];
[LinphoneManager setValueInMessageAppData:nil forKey:@"localfile" inMessage:msg];
}
events = events->next;
}

View file

@ -13,6 +13,7 @@
@interface FileTransferDelegate : NSObject
- (void)upload:(UIImage *)image withURL:(NSURL *)url forChatRoom:(LinphoneChatRoom *)chatRoom withQuality:(float)quality;
- (void)uploadFile:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom withUrl:(NSURL *)url;
- (void)cancel;
- (BOOL)download:(LinphoneChatMessage *)message;
- (void)stopAndDestroy;

View file

@ -44,62 +44,63 @@ static void linphone_iphone_file_transfer_recv(LinphoneChatMessage *message, con
if (size == 0) {
LOGI(@"Transfer of %s (%d bytes): download finished", linphone_content_get_name(content), size);
assert([thiz.data length] == linphone_content_get_file_size(content));
NSString *fileType = [NSString stringWithUTF8String:linphone_content_get_type(content)];
if ([fileType isEqualToString:@"image"]) {
// we're finished, save the image and update the message
UIImage *image = [UIImage imageWithData:thiz.data];
if (!image) {
UIAlertController *errView = [UIAlertController
alertControllerWithTitle:NSLocalizedString(@"File download error", nil)
message:NSLocalizedString(@"Error while downloading the file.\n"
@"The file is probably encrypted.\n"
@"Please retry to download this file after activating LIME.",
nil)
preferredStyle:UIAlertControllerStyleAlert];
// we're finished, save the image and update the message
UIImage *image = [UIImage imageWithData:thiz.data];
if (!image) {
UIAlertController *errView = [UIAlertController
alertControllerWithTitle:NSLocalizedString(@"File download error", nil)
message:NSLocalizedString(@"Error while downloading the file.\n"
@"The file is probably encrypted.\n"
@"Please retry to download this file after activating LIME.",
nil)
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:@"OK"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action){
}];
UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:@"OK"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action){
}];
[errView addAction:defaultAction];
[PhoneMainView.instance presentViewController:errView animated:YES completion:nil];
[thiz stopAndDestroy];
return;
}
[errView addAction:defaultAction];
[PhoneMainView.instance presentViewController:errView animated:YES completion:nil];
[thiz stopAndDestroy];
return;
}
CFBridgingRetain(thiz);
[[LinphoneManager.instance fileTransferDelegates] removeObject:thiz];
CFBridgingRetain(thiz);
[[LinphoneManager.instance fileTransferDelegates] removeObject:thiz];
// until image is properly saved, keep a reminder on it so that the
// chat bubble is aware of the fact that image is being saved to device
[LinphoneManager setValueInMessageAppData:@"saving..." forKey:@"localimage" inMessage:message];
// until image is properly saved, keep a reminder on it so that the
// chat bubble is aware of the fact that image is being saved to device
[LinphoneManager setValueInMessageAppData:@"saving..." forKey:@"localimage" inMessage:message];
[LinphoneManager.instance.photoLibrary
writeImageToSavedPhotosAlbum:image.CGImage
orientation:(ALAssetOrientation)[image imageOrientation]
completionBlock:^(NSURL *assetURL, NSError *error) {
if (error) {
LOGE(@"Cannot save image data downloaded [%@]", [error localizedDescription]);
[LinphoneManager setValueInMessageAppData:nil forKey:@"localimage" inMessage:message];
UIAlertController *errView = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Transfer error", nil)
[LinphoneManager.instance.photoLibrary
writeImageToSavedPhotosAlbum:image.CGImage
orientation:(ALAssetOrientation)[image imageOrientation]
completionBlock:^(NSURL *assetURL, NSError *error) {
if (error) {
LOGE(@"Cannot save image data downloaded [%@]", [error localizedDescription]);
[LinphoneManager setValueInMessageAppData:nil forKey:@"localimage" inMessage:message];
UIAlertController *errView = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Transfer error", nil)
message:NSLocalizedString(@"Cannot write image to photo library",
nil)
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK"
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {}];
[errView addAction:defaultAction];
[PhoneMainView.instance presentViewController:errView animated:YES completion:nil];
} else {
LOGI(@"Image saved to [%@]", [assetURL absoluteString]);
[LinphoneManager setValueInMessageAppData:[assetURL absoluteString]
[errView addAction:defaultAction];
[PhoneMainView.instance presentViewController:errView animated:YES completion:nil];
} else {
LOGI(@"Image saved to [%@]", [assetURL absoluteString]);
[LinphoneManager setValueInMessageAppData:[assetURL absoluteString]
forKey:@"localimage"
inMessage:message];
}
[NSNotificationCenter.defaultCenter
postNotificationName:kLinphoneFileTransferRecvUpdate
}
[NSNotificationCenter.defaultCenter
postNotificationName:kLinphoneFileTransferRecvUpdate
object:thiz
userInfo:@{
@"state" : @(LinphoneChatMessageStateDelivered), // we dont want to
@ -109,10 +110,38 @@ static void linphone_iphone_file_transfer_recv(LinphoneChatMessage *message, con
@"progress" : @(1.f),
}];
[thiz stopAndDestroy];
CFRelease((__bridge CFTypeRef)thiz);
}];
} else {
[thiz stopAndDestroy];
CFRelease((__bridge CFTypeRef)thiz);
}];
} else {
[[LinphoneManager.instance fileTransferDelegates] removeObject:thiz];
NSString *key = [fileType isEqualToString:@"file"] ? @"localfile" : @"localvideo";
NSString *name =[NSString stringWithUTF8String:linphone_content_get_name(content)];
[LinphoneManager setValueInMessageAppData:@"saving..." forKey:key inMessage:message];
//write file to path
dispatch_async(dispatch_get_main_queue(), ^{
NSString *filePath = [LinphoneManager documentFile:name];
[[NSFileManager defaultManager] createFileAtPath:filePath
contents:thiz.data
attributes:nil];
[LinphoneManager setValueInMessageAppData:name forKey:key inMessage:message];
[NSNotificationCenter.defaultCenter
postNotificationName:kLinphoneFileTransferRecvUpdate
object:thiz
userInfo:@{
@"state" : @(LinphoneChatMessageStateDelivered), // we dont want to trigger
@"progress" : @(1.f), // FileTransferDone here
}];
[thiz stopAndDestroy];
});
}
} else {
LOGD(@"Transfer of %s (%d bytes): already %ld sent, adding %ld", linphone_content_get_name(content),
linphone_content_get_file_size(content), [thiz.data length], size);
[thiz.data appendBytes:linphone_buffer_get_string_content(buffer) length:size];
@ -124,6 +153,7 @@ static void linphone_iphone_file_transfer_recv(LinphoneChatMessage *message, con
@"progress" : @([thiz.data length] * 1.f / linphone_content_get_file_size(content)),
}];
}
}
static LinphoneBuffer *linphone_iphone_file_transfer_send(LinphoneChatMessage *message, const LinphoneContent *content,
@ -166,38 +196,58 @@ static LinphoneBuffer *linphone_iphone_file_transfer_send(LinphoneChatMessage *m
return NULL;
}
- (void)upload:(UIImage *)image withURL:(NSURL *)url forChatRoom:(LinphoneChatRoom *)chatRoom withQuality:(float)quality {
[LinphoneManager.instance.fileTransferDelegates addObject:self];
- (void)uploadData:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom type:(NSString *)type subtype:(NSString *)subtype name:(NSString *)name key:(NSString *)key keyData:(NSString *)keyData qualityData:(NSNumber *)qualityData {
[LinphoneManager.instance.fileTransferDelegates addObject:self];
LinphoneContent *content = linphone_core_create_content(linphone_chat_room_get_core(chatRoom));
_data = [NSMutableData dataWithData:data];
linphone_content_set_type(content, [type UTF8String]);
linphone_content_set_subtype(content, [subtype UTF8String]);
linphone_content_set_name(content, [name UTF8String]);
linphone_content_set_size(content, _data.length);
_message = linphone_chat_room_create_file_transfer_message(chatRoom, content);
linphone_content_unref(content);
linphone_chat_message_cbs_set_file_transfer_send(linphone_chat_message_get_callbacks(_message),
linphone_iphone_file_transfer_send);
LinphoneContent *content = linphone_core_create_content(linphone_chat_room_get_core(chatRoom));
_data = [NSMutableData dataWithData:UIImageJPEGRepresentation(image, quality)];
linphone_content_set_type(content, "image");
linphone_content_set_subtype(content, "jpeg");
linphone_content_set_name(
content, [[NSString stringWithFormat:@"%li-%f.jpg", (long)image.hash, [NSDate timeIntervalSinceReferenceDate]]
UTF8String]);
linphone_content_set_size(content, _data.length);
_message = linphone_chat_room_create_file_transfer_message(chatRoom, content);
linphone_content_unref(content);
linphone_chat_message_cbs_set_file_transfer_send(linphone_chat_message_get_callbacks(_message),
linphone_iphone_file_transfer_send);
if (url) {
// internal url is saved in the appdata for display and later save
[LinphoneManager setValueInMessageAppData:[url absoluteString] forKey:@"localimage" inMessage:_message];
[LinphoneManager setValueInMessageAppData:[NSNumber numberWithFloat:quality] forKey:@"uploadQuality" inMessage:_message];
}
LOGI(@"%p Uploading content from message %p", self, _message);
linphone_chat_room_send_chat_message(chatRoom, _message);
if (linphone_core_lime_enabled(LC) == LinphoneLimeMandatory && !linphone_chat_room_lime_available(chatRoom)) {
[LinphoneManager.instance alertLIME:chatRoom];
}
// internal url is saved in the appdata for display and later save
[LinphoneManager setValueInMessageAppData:keyData forKey:key inMessage:_message];
[LinphoneManager setValueInMessageAppData:qualityData forKey:@"uploadQuality" inMessage:_message];
LOGI(@"%p Uploading content from message %p", self, _message);
linphone_chat_room_send_chat_message(chatRoom, _message);
if (linphone_core_lime_enabled(LC) == LinphoneLimeMandatory && !linphone_chat_room_lime_available(chatRoom)) {
[LinphoneManager.instance alertLIME:chatRoom];
}
}
- (void)upload:(UIImage *)image withURL:(NSURL *)url forChatRoom:(LinphoneChatRoom *)chatRoom withQuality:(float)quality {
NSString *name = [NSString stringWithFormat:@"%li-%f.jpg", (long)image.hash, [NSDate timeIntervalSinceReferenceDate]];
if (url)
[self uploadData:UIImageJPEGRepresentation(image, quality) forChatRoom:chatRoom type:@"image" subtype:@"jpeg" name:name key:@"localimage" keyData:[url absoluteString] qualityData:[NSNumber numberWithFloat:quality]];
else
[self uploadData:UIImageJPEGRepresentation(image, quality) forChatRoom:chatRoom type:@"image" subtype:@"jpeg" name:name key:@"localimage" keyData:nil qualityData:nil];
}
- (void)uploadFile:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom withUrl:(NSURL *)url {
NSString *name = [url lastPathComponent];
//save file to Documents
NSString *filePath = [LinphoneManager documentFile:name];
[[NSFileManager defaultManager] createFileAtPath:filePath
contents:[NSMutableData dataWithData:data]
attributes:nil];
if ([[url pathExtension] isEqualToString:@"MOV"])
[self uploadData:data forChatRoom:chatRoom type:nil subtype:nil name:name key:@"localvideo" keyData:name qualityData:nil];
else
[self uploadData:data forChatRoom:chatRoom type:@"file" subtype:nil name:name key:@"localfile" keyData:name qualityData:nil];
}
- (BOOL)download:(LinphoneChatMessage *)message {
[[LinphoneManager.instance fileTransferDelegates] addObject:self];

View file

@ -68,6 +68,7 @@ typedef enum {
- (UIImage *)squareCrop;
- (UIImage *)scaleToSize:(CGSize)size squared:(BOOL)squared;
+ (UIImage *)resizeImage:(UIImage *)image withMaxWidth:(float)maxWidth andMaxHeight:(float)maxHeight;
@end

View file

@ -644,6 +644,36 @@
return scaledImage;
}
+ (UIImage *)resizeImage:(UIImage *)image withMaxWidth:(float)maxWidth andMaxHeight:(float)maxHeight {
float actualHeight = image.size.height;
float actualWidth = image.size.width;
float imgRatio = actualWidth / actualHeight;
float maxRatio = maxWidth / maxHeight;
float compressionQuality = 1;
if (actualHeight > maxHeight || actualWidth > maxWidth)
{
if (imgRatio < maxRatio) {
imgRatio = maxHeight / actualHeight;
actualWidth = imgRatio * actualWidth;
actualHeight = maxHeight;
} else if (imgRatio > maxRatio) {
imgRatio = maxWidth / actualWidth;
actualHeight = imgRatio * actualHeight;
actualWidth = maxWidth;
} else {
actualHeight = maxHeight;
actualWidth = maxWidth;
}
}
CGRect rect = CGRectMake(0.0, 0.0, actualWidth, actualHeight);
UIGraphicsBeginImageContext(rect.size);
[image drawInRect:rect];
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
NSData *imageData = UIImageJPEGRepresentation(img, compressionQuality);
UIGraphicsEndImageContext();
return [UIImage imageWithData:imageData];
}
@end
@implementation UIColor (LightAndDark)

View file

@ -45,6 +45,7 @@ tcp_tls_keepalive=30000
history_max_size=-1
enable_basic_to_client_group_chat_room_migration=0
aggregate_imdn=1
version_check_url_root=https://linphone.org/releases
[sound]
dtmf_player_amp=0.007

View file

@ -0,0 +1,153 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="M4Y-Lb-cyx">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Today View Controller-->
<scene sceneID="cwh-vc-ff4">
<objects>
<viewController id="M4Y-Lb-cyx" customClass="TodayViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" simulatedAppContext="notificationCenter" id="S3S-Oj-5AN">
<rect key="frame" x="0.0" y="0.0" width="320" height="37"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" distribution="equalSpacing" translatesAutoresizingMaskIntoConstraints="NO" id="acy-Dv-HTU">
<rect key="frame" x="16" y="-25" width="288" height="87.5"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="ndj-Mq-Hl7">
<rect key="frame" x="0.0" y="0.0" width="60" height="87.5"/>
<subviews>
<button opaque="NO" contentMode="scaleAspectFit" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Wm5-Qb-p3S">
<rect key="frame" x="0.0" y="0.0" width="60" height="60.5"/>
<constraints>
<constraint firstAttribute="height" constant="60" id="0Ak-Qk-GO3"/>
<constraint firstAttribute="width" constant="60" id="ZqS-ba-Lu5"/>
</constraints>
<state key="normal" image="avatar.png"/>
<connections>
<action selector="firstButtonTapped" destination="M4Y-Lb-cyx" eventType="touchUpInside" id="hiI-LE-Zgn"/>
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" " textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ZCh-k6-wlC">
<rect key="frame" x="0.0" y="70.5" width="60" height="17"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="ZCh-k6-wlC" firstAttribute="centerX" secondItem="ndj-Mq-Hl7" secondAttribute="centerX" id="7gN-E9-QSN"/>
</constraints>
</stackView>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="aDd-1O-GG5">
<rect key="frame" x="76" y="0.0" width="60" height="87.5"/>
<subviews>
<button opaque="NO" contentMode="scaleAspectFit" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="YV6-7A-P4J">
<rect key="frame" x="0.0" y="0.0" width="60" height="60"/>
<constraints>
<constraint firstAttribute="height" constant="60" id="7Mf-cY-51s"/>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="60" id="b1f-XQ-h9r"/>
<constraint firstAttribute="width" constant="60" id="pF3-4V-DIz"/>
</constraints>
<state key="normal" image="avatar.png"/>
<connections>
<action selector="secondButtonTapped" destination="M4Y-Lb-cyx" eventType="touchUpInside" id="VD4-JL-D57"/>
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" " textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vzp-zG-86G">
<rect key="frame" x="0.0" y="70" width="60" height="17.5"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="vzp-zG-86G" firstAttribute="centerX" secondItem="aDd-1O-GG5" secondAttribute="centerX" id="Reb-Z7-yZx"/>
</constraints>
</stackView>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="Jda-ED-Hck">
<rect key="frame" x="152" y="0.0" width="60" height="87.5"/>
<subviews>
<button opaque="NO" contentMode="scaleAspectFit" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="R1m-4P-62J">
<rect key="frame" x="0.0" y="0.0" width="60" height="60"/>
<constraints>
<constraint firstAttribute="height" constant="60" id="Jwm-Hh-flO"/>
<constraint firstAttribute="width" constant="60" id="Lv0-kT-oOL"/>
</constraints>
<state key="normal" image="avatar.png"/>
<connections>
<action selector="thirdButtonTapped" destination="M4Y-Lb-cyx" eventType="touchUpInside" id="A6r-gZ-0Be"/>
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" " textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1km-2d-H8g">
<rect key="frame" x="0.0" y="70" width="60" height="17.5"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="1km-2d-H8g" firstAttribute="centerX" secondItem="Jda-ED-Hck" secondAttribute="centerX" id="Jaw-30-wJW"/>
</constraints>
</stackView>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="Vem-dd-m97">
<rect key="frame" x="228" y="0.0" width="60" height="87.5"/>
<subviews>
<button opaque="NO" contentMode="scaleAspectFit" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="EgK-EE-9vt">
<rect key="frame" x="0.0" y="0.0" width="60" height="60"/>
<constraints>
<constraint firstAttribute="width" constant="60" id="T6s-2z-6yf"/>
<constraint firstAttribute="height" constant="60" id="yoB-Mx-9iw"/>
</constraints>
<state key="normal" image="avatar.png"/>
<connections>
<action selector="fourthButtonTapped" destination="M4Y-Lb-cyx" eventType="touchUpInside" id="MfV-kR-GEH"/>
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" " textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Z4D-W4-T9E">
<rect key="frame" x="0.0" y="70" width="60" height="17.5"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="Z4D-W4-T9E" firstAttribute="centerX" secondItem="Vem-dd-m97" secondAttribute="centerX" id="TU4-tO-i4b"/>
</constraints>
</stackView>
</subviews>
</stackView>
</subviews>
<constraints>
<constraint firstItem="acy-Dv-HTU" firstAttribute="centerX" secondItem="S3S-Oj-5AN" secondAttribute="centerX" id="Thu-fq-hKU"/>
<constraint firstItem="acy-Dv-HTU" firstAttribute="centerY" secondItem="S3S-Oj-5AN" secondAttribute="centerY" id="Wku-rc-Ij6"/>
<constraint firstItem="acy-Dv-HTU" firstAttribute="leading" secondItem="S3S-Oj-5AN" secondAttribute="leadingMargin" id="fWT-EJ-zhi"/>
</constraints>
<viewLayoutGuide key="safeArea" id="ssy-KU-ocm"/>
</view>
<extendedEdge key="edgesForExtendedLayout"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<size key="freeformSize" width="320" height="37"/>
<connections>
<outletCollection property="stackViews" destination="ndj-Mq-Hl7" id="NTd-tl-hho"/>
<outletCollection property="stackViews" destination="aDd-1O-GG5" id="y3m-I8-kYi"/>
<outletCollection property="stackViews" destination="Jda-ED-Hck" id="VR1-hK-wc3"/>
<outletCollection property="stackViews" destination="Vem-dd-m97" id="9Eh-UN-U3f"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="vXp-U4-Rya" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
<resources>
<image name="avatar.png" width="259" height="259"/>
</resources>
</document>

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Linphone - Latest Calls</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionMainStoryboard</key>
<string>MainInterface</string>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.widget-extension</string>
</dict>
</dict>
</plist>

View file

@ -0,0 +1,20 @@
//
// TodayViewController.h
// latestCallsWidget
//
// Created by David Idmansour on 06/06/2018.
//
#import <UIKit/UIKit.h>
@interface TodayViewController : UIViewController
@property (strong, nonatomic) IBOutletCollection(UIStackView) NSArray *stackViews;
@property (strong, nonatomic) NSMutableArray *imgs;
@property (strong, nonatomic) NSMutableArray *logIds;
@property (strong, nonatomic) NSMutableArray *displayNames;
- (IBAction)firstButtonTapped;
- (IBAction)secondButtonTapped;
- (IBAction)thirdButtonTapped;
- (IBAction)fourthButtonTapped;
@end

View file

@ -0,0 +1,109 @@
//
// TodayViewController.m
// latestCallsWidget
//
// Created by David Idmansour on 06/06/2018.
//
#import "TodayViewController.h"
#import <NotificationCenter/NotificationCenter.h>
@interface TodayViewController () <NCWidgetProviding>
@end
@implementation TodayViewController
- (void)loadData {
NSUserDefaults *mySharedDefaults = [[NSUserDefaults alloc] initWithSuiteName: @"group.belledonne-communications.linphone.widget"];
[_imgs removeAllObjects];
[_logIds removeAllObjects];
[_displayNames removeAllObjects];
NSMutableArray *logs = [NSMutableArray arrayWithArray:[mySharedDefaults objectForKey:@"logs"]];
for (NSDictionary *dict in logs) {
[_logIds addObject:[dict objectForKey:@"id"]];
[_displayNames addObject:[dict objectForKey:@"display"]];
[_imgs addObject:[dict objectForKey:@"img"]?:[NSNull null]];
}
}
- (void)draw {
int i = 0;
for (i = 0 ; i < 4 && i < _logIds.count ; i++) {
UIStackView *stack = _stackViews[i];
UIButton *button = stack.subviews[0];
UILabel *name = stack.subviews[1];
if (_imgs[i] != [NSNull null]) {
[button setImage:[UIImage imageWithData:_imgs[i]] forState:UIControlStateNormal];
}
[stack setAlpha:1];
button.enabled = YES;
[name setText:_displayNames[i]];
}
while (i < 4) {
UIStackView *stack = _stackViews[i];
UIButton *button = stack.subviews[0];
[stack setAlpha:0];
button.enabled = NO;
i++;
}
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
for (UIStackView *stack in _stackViews) {
UIButton *button = stack.subviews[0];
// making rounded image
UIImageView *imageView = button.imageView;
imageView.layer.cornerRadius = imageView.frame.size.height / 2;
imageView.clipsToBounds = YES;
}
_logIds = [NSMutableArray array];
_imgs = [NSMutableArray array];
_displayNames = [NSMutableArray array];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
[_imgs removeAllObjects];
}
- (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler {
// Perform any setup necessary in order to update the view.
// If an error is encountered, use NCUpdateResultFailed
// If there's no update required, use NCUpdateResultNoData
// If there's an update, use NCUpdateResultNewData
[self loadData];
[self draw];
completionHandler(NCUpdateResultNewData);
}
- (void)launchAppWithURL:(NSURL *) url {
[self.extensionContext openURL:url
completionHandler:nil];
}
- (void)launchOnHistoryDetailsWithId:(NSString *)logId {
[self launchAppWithURL:[NSURL URLWithString:[@"linphone-widget://call_log/show?" stringByAppendingString:logId]]];
}
- (IBAction)firstButtonTapped {
[self launchOnHistoryDetailsWithId:_logIds[0]];
}
- (IBAction)secondButtonTapped {
[self launchOnHistoryDetailsWithId:_logIds[1]];
}
- (IBAction)thirdButtonTapped {
[self launchOnHistoryDetailsWithId:_logIds[2]];
}
- (IBAction)fourthButtonTapped {
[self launchOnHistoryDetailsWithId:_logIds[3]];
}
@end

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.belledonne-communications.linphone.widget</string>
</array>
</dict>
</plist>

View file

@ -0,0 +1,152 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="M4Y-Lb-cyx">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Today View Controller-->
<scene sceneID="cwh-vc-ff4">
<objects>
<viewController id="M4Y-Lb-cyx" customClass="TodayViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" simulatedAppContext="notificationCenter" id="S3S-Oj-5AN">
<rect key="frame" x="0.0" y="0.0" width="320" height="37"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" distribution="equalSpacing" translatesAutoresizingMaskIntoConstraints="NO" id="kvZ-RU-0JV">
<rect key="frame" x="16" y="-26" width="288" height="87.5"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="P4U-hP-N9F">
<rect key="frame" x="0.0" y="0.0" width="60" height="87.5"/>
<subviews>
<button opaque="NO" contentMode="scaleAspectFit" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="dGY-Wo-tng">
<rect key="frame" x="0.0" y="0.0" width="60" height="60.5"/>
<constraints>
<constraint firstAttribute="width" constant="60" id="a43-Du-y1b"/>
<constraint firstAttribute="height" constant="60" id="gNd-AG-oLn"/>
</constraints>
<state key="normal" image="avatar.png"/>
<connections>
<action selector="firstButtonTapped" destination="M4Y-Lb-cyx" eventType="touchUpInside" id="n6M-cY-cWU"/>
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" " textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fpJ-Wl-BT1">
<rect key="frame" x="0.0" y="70.5" width="60" height="17"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="fpJ-Wl-BT1" firstAttribute="centerX" secondItem="P4U-hP-N9F" secondAttribute="centerX" id="3Zz-Mm-hxc"/>
</constraints>
</stackView>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="79U-UR-Bp2">
<rect key="frame" x="76" y="0.0" width="60" height="87.5"/>
<subviews>
<button opaque="NO" contentMode="scaleAspectFit" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="aiZ-1q-Ssd">
<rect key="frame" x="0.0" y="0.0" width="60" height="60"/>
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="60" id="0Me-vh-jt4"/>
<constraint firstAttribute="height" constant="60" id="9hj-Pa-s8F"/>
<constraint firstAttribute="width" constant="60" id="iil-bF-24j"/>
</constraints>
<state key="normal" image="avatar.png"/>
<connections>
<action selector="secondButtonTapped" destination="M4Y-Lb-cyx" eventType="touchUpInside" id="e09-4G-NJf"/>
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" " textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fEo-fa-uyD">
<rect key="frame" x="0.0" y="70" width="60" height="17.5"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="fEo-fa-uyD" firstAttribute="centerX" secondItem="79U-UR-Bp2" secondAttribute="centerX" id="FPk-ST-xe3"/>
</constraints>
</stackView>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="Xgm-go-u3S">
<rect key="frame" x="152" y="0.0" width="60" height="87.5"/>
<subviews>
<button opaque="NO" contentMode="scaleAspectFit" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="VgA-lq-CQL">
<rect key="frame" x="0.0" y="0.0" width="60" height="60"/>
<constraints>
<constraint firstAttribute="width" constant="60" id="TEZ-z7-Yce"/>
<constraint firstAttribute="height" constant="60" id="rtD-OM-bmH"/>
</constraints>
<state key="normal" image="avatar.png"/>
<connections>
<action selector="thirdButtonTapped" destination="M4Y-Lb-cyx" eventType="touchUpInside" id="OtO-nr-X1J"/>
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" " textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zM6-aO-Pes">
<rect key="frame" x="0.0" y="70" width="60" height="17.5"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="zM6-aO-Pes" firstAttribute="centerX" secondItem="Xgm-go-u3S" secondAttribute="centerX" id="lGV-8K-Ol0"/>
</constraints>
</stackView>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="nHe-ID-vxE">
<rect key="frame" x="228" y="0.0" width="60" height="87.5"/>
<subviews>
<button opaque="NO" contentMode="scaleAspectFit" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="7je-G1-IfQ">
<rect key="frame" x="0.0" y="0.0" width="60" height="60"/>
<constraints>
<constraint firstAttribute="width" constant="60" id="Pm8-lw-PB4"/>
<constraint firstAttribute="height" constant="60" id="SYf-Yl-EDf"/>
</constraints>
<state key="normal" image="avatar.png"/>
<connections>
<action selector="fourthButtonTapped" destination="M4Y-Lb-cyx" eventType="touchUpInside" id="a2u-BT-Fkp"/>
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" " textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bpY-VL-FDz">
<rect key="frame" x="0.0" y="70" width="60" height="17.5"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="bpY-VL-FDz" firstAttribute="centerX" secondItem="nHe-ID-vxE" secondAttribute="centerX" id="PD7-gj-AXr"/>
</constraints>
</stackView>
</subviews>
</stackView>
</subviews>
<constraints>
<constraint firstItem="kvZ-RU-0JV" firstAttribute="centerY" secondItem="S3S-Oj-5AN" secondAttribute="centerY" id="1dD-eI-ohO"/>
<constraint firstItem="kvZ-RU-0JV" firstAttribute="leading" secondItem="S3S-Oj-5AN" secondAttribute="leading" constant="16" id="9XT-er-59s"/>
<constraint firstItem="kvZ-RU-0JV" firstAttribute="centerX" secondItem="S3S-Oj-5AN" secondAttribute="centerX" id="l30-a1-mDN"/>
</constraints>
<viewLayoutGuide key="safeArea" id="ssy-KU-ocm"/>
</view>
<extendedEdge key="edgesForExtendedLayout"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<size key="freeformSize" width="320" height="37"/>
<connections>
<outletCollection property="stackViews" destination="P4U-hP-N9F" id="3f1-th-6ZO"/>
<outletCollection property="stackViews" destination="79U-UR-Bp2" id="ix2-7S-Rim"/>
<outletCollection property="stackViews" destination="Xgm-go-u3S" id="VYM-Ld-joW"/>
<outletCollection property="stackViews" destination="nHe-ID-vxE" id="iGd-Ix-Pj6"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="vXp-U4-Rya" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
<resources>
<image name="avatar.png" width="259" height="259"/>
</resources>
</document>

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Linphone - Latest Chatrooms</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionMainStoryboard</key>
<string>MainInterface</string>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.widget-extension</string>
</dict>
</dict>
</plist>

View file

@ -0,0 +1,22 @@
//
// TodayViewController.h
// latestChatroomsWidget
//
// Created by David Idmansour on 18/06/2018.
//
#import <UIKit/UIKit.h>
@interface TodayViewController : UIViewController
@property (strong, nonatomic) IBOutletCollection(UIStackView) NSArray *stackViews;
@property (strong, nonatomic) NSMutableArray *addresses;
@property (strong, nonatomic) NSMutableArray *localAddress;
@property (strong, nonatomic) NSMutableArray *displayNames;
@property (strong, nonatomic) NSMutableArray *isConf;
@property (strong, nonatomic) NSMutableArray *imgs;
- (IBAction)firstButtonTapped;
- (IBAction)secondButtonTapped;
- (IBAction)thirdButtonTapped;
- (IBAction)fourthButtonTapped;
@end

View file

@ -0,0 +1,119 @@
//
// TodayViewController.m
// latestChatroomsWidget
//
// Created by David Idmansour on 18/06/2018.
//
#import "TodayViewController.h"
#import <NotificationCenter/NotificationCenter.h>
@interface TodayViewController () <NCWidgetProviding>
@end
@implementation TodayViewController
- (void)loadData {
NSUserDefaults *mySharedDefaults = [[NSUserDefaults alloc] initWithSuiteName: @"group.belledonne-communications.linphone.widget"];
[_imgs removeAllObjects];
[_addresses removeAllObjects];
[_localAddress removeAllObjects];
[_displayNames removeAllObjects];
[_isConf removeAllObjects];
NSMutableArray *chatrooms = [NSMutableArray arrayWithArray:[mySharedDefaults objectForKey:@"chatrooms"]];
for (NSDictionary *dict in chatrooms) {
[_addresses addObject:[dict objectForKey:@"peer"]];
[_localAddress addObject:[dict objectForKey:@"local"]];
[_displayNames addObject:[dict objectForKey:@"display"]];
[_isConf addObject:(NSNumber *)[dict objectForKey:@"nbParticipants"]];
[_imgs addObject:[dict objectForKey:@"img"]?:[NSNull null]];
}
}
- (void)draw {
int i = 0;
for (i = 0 ; i < 4 && i < _addresses.count ; i++) {
UIStackView *stack = _stackViews[i];
UIButton *button = stack.subviews[0];
UILabel *name = stack.subviews[1];
if (_imgs[i] != [NSNull null]) {
[button setImage:[UIImage imageWithData:_imgs[i]] forState:UIControlStateNormal];
} else if (((NSNumber *)_isConf[i]).boolValue) {
[button setImage:[UIImage imageNamed:@"chat_group_avatar.png"] forState:UIControlStateNormal];
}
[stack setAlpha:1];
button.enabled = YES;
[name setText:_displayNames[i]];
}
while (i < 4) {
UIStackView *stack = _stackViews[i];
UIButton *button = stack.subviews[0];
[stack setAlpha:0];
button.enabled = NO;
i++;
}
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
for (UIStackView *stack in _stackViews) {
UIButton *button = stack.subviews[0];
// making rounded image
UIImageView *imageView = button.imageView;
imageView.layer.cornerRadius = imageView.frame.size.height / 2;
imageView.clipsToBounds = YES;
}
_addresses = [NSMutableArray array];
_localAddress = [NSMutableArray array];
_displayNames = [NSMutableArray array];
_imgs = [NSMutableArray array];
_isConf = [NSMutableArray array];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
[_imgs removeAllObjects];
}
- (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler {
// Perform any setup necessary in order to update the view.
// If an error is encountered, use NCUpdateResultFailed
// If there's no update required, use NCUpdateResultNoData
// If there's an update, use NCUpdateResultNewData
[self loadData];
[self draw];
completionHandler(NCUpdateResultNewData);
}
- (void)launchAppWithURL:(NSURL *) url {
[self.extensionContext openURL:url
completionHandler:nil];
}
- (void)launchOnChatConversationViewWithPeerAddress:(NSString *)peerAddress andLocalAddress:(NSString *)localAddress {
[self launchAppWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"linphone-widget://chatroom/show?peer=%@&local=%@", peerAddress, localAddress]]];
}
- (IBAction)firstButtonTapped {
[self launchOnChatConversationViewWithPeerAddress:_addresses[0] andLocalAddress:_localAddress[0]];
}
- (IBAction)secondButtonTapped {
[self launchOnChatConversationViewWithPeerAddress:_addresses[1] andLocalAddress:_localAddress[1]];
}
- (IBAction)thirdButtonTapped {
[self launchOnChatConversationViewWithPeerAddress:_addresses[2] andLocalAddress:_localAddress[2]];
}
- (IBAction)fourthButtonTapped {
[self launchOnChatConversationViewWithPeerAddress:_addresses[3] andLocalAddress:_localAddress[3]];
}
@end

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.belledonne-communications.linphone.widget</string>
</array>
</dict>
</plist>

View file

@ -51,6 +51,30 @@
<string>linphone-config</string>
</array>
</dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLIconFile</key>
<string>linphone_icon_72@2x</string>
<key>CFBundleURLName</key>
<string>org.linphone.phone</string>
<key>CFBundleURLSchemes</key>
<array>
<string>message-linphone</string>
</array>
</dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>CFBundleURLIconFile</key>
<string>linphone_icon_72@2x</string>
<key>CFBundleURLName</key>
<string>org.linphone.phone</string>
<key>CFBundleURLSchemes</key>
<array>
<string>linphone-widget</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>1</string>
@ -76,6 +100,19 @@
<string>Make audio/video calls</string>
<key>UIApplicationExitsOnSuspend</key>
<false/>
<key>UIApplicationShortcutItems</key>
<array>
<dict>
<key>UIApplicationShortcutItemIconType</key>
<string>UIApplicationShortcutIconTypeCompose</string>
<key>UIApplicationShortcutItemTitle</key>
<string>New Message</string>
<key>UIApplicationShortcutItemType</key>
<string>linphone.phone.action.newMessage</string>
</dict>
</array>
<key>UIApplicationShortcutWidget</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).widget.latestChatrooms</string>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>

View file

@ -4,5 +4,10 @@
<dict>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.security.application-groups</key>
<array>
<string>group.belledonne-communications.linphone.widget</string>
<string>group.belledonne-communications.linphone</string>
</array>
</dict>
</plist>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" systemVersion="17A278a" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="j1y-V4-xli">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Share View Controller-->
<scene sceneID="ceB-am-kn3">
<objects>
<viewController id="j1y-V4-xli" customClass="ShareViewController" customModuleProvider="" sceneMemberID="viewController">
<view key="view" opaque="NO" contentMode="scaleToFill" id="wbc-yd-nQP">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<viewLayoutGuide key="safeArea" id="1Xd-am-t49"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="CEy-Cv-SGf" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>

View file

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>linphone</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>NSExtensionActivationRule</key>
<string>TRUEPREDICATE</string>
</dict>
<key>NSExtensionMainStoryboard</key>
<string>MainInterface</string>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.share-services</string>
</dict>
</dict>
</plist>

View file

@ -0,0 +1,13 @@
//
// ShareViewController.h
// linphoneExtension
//
// Created by Danmei Chen on 31/05/2018.
//
#import <UIKit/UIKit.h>
#import <Social/Social.h>
@interface ShareViewController : SLComposeServiceViewController
@end

View file

@ -0,0 +1,99 @@
//
// ShareViewController.m
// linphoneExtension
//
// Created by Danmei Chen on 31/05/2018.
//
#import "ShareViewController.h"
@interface ShareViewController ()
@end
static NSString* groupName = @"group.belledonne-communications.linphone";
@implementation ShareViewController
- (BOOL)isContentValid {
// Do validation of contentText and/or NSExtensionContext attachments here
return YES;
}
- (void)didSelectPost {
// This is called after the user selects Post. Do the upload of contentText and/or NSExtensionContext attachments.
// Inform the host that we're done, so it un-blocks its UI. Note: Alternatively you could call super's -didSelectPost, which will similarly complete the extension context.
for (NSExtensionItem *item in self.extensionContext.inputItems) {
for (NSItemProvider *provider in item.attachments) {
NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:groupName];
if ([provider hasItemConformingToTypeIdentifier:@"public.jpeg"]) {
[self loadItem:provider typeIdentifier:@"public.jpeg" defaults:defaults key:@"img"];
} else if ([provider hasItemConformingToTypeIdentifier:@"public.url"]) {
[self loadItem:provider typeIdentifier:@"public.url" defaults:defaults key:@"web"];
} else if ([provider hasItemConformingToTypeIdentifier:@"public.movie"]) {
[self loadItem:provider typeIdentifier:@"public.movie" defaults:defaults key:@"mov"];
} else if ([provider hasItemConformingToTypeIdentifier:@"public.plain-text"]) {
[self loadItem:provider typeIdentifier:@"public.plain-text" defaults:defaults key:@"text"];
} else if ([provider hasItemConformingToTypeIdentifier:@"com.adobe.pdf"]) {
[self loadItem:provider typeIdentifier:@"com.adobe.pdf" defaults:defaults key:@"web"];
}
/*else if ([provider hasItemConformingToTypeIdentifier:@"public.png"]) {
[self loadItem:provider typeIdentifier:@"public.png" defaults:defaults key:@"img"];
}*/
else{
NSLog(@"Unkown itemprovider = %@", provider);
[self.extensionContext completeRequestReturningItems:@[] completionHandler:nil];
}
}
}
}
- (NSArray *)configurationItems {
// To add configuration options via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here.
return @[];
}
- (void)loadItem:(NSItemProvider *)provider typeIdentifier:(NSString *)typeIdentifier defaults:(NSUserDefaults *)defaults key:(NSString *)key {
[provider loadItemForTypeIdentifier:typeIdentifier options:nil completionHandler:^(id<NSSecureCoding> _Nullable item, NSError * _Null_unspecified error) {
if([(NSObject*)item isKindOfClass:[NSURL class]]) {
NSData *nsData = [NSData dataWithContentsOfURL:(NSURL*)item];
if (nsData) {
NSDictionary *dict = @{@"nsData" : nsData,
@"url" : [(NSURL*)item absoluteString]};
[defaults setObject:dict forKey:key];
} else {
NSLog(@"NSExtensionItem Error, provider = %@", provider);
[self.extensionContext completeRequestReturningItems:@[] completionHandler:nil];
}
}
else if ([(NSObject*)item isKindOfClass:[UIImage class]]) {
NSData *imgData = UIImagePNGRepresentation((UIImage*)item);
if (imgData) {
NSDictionary *dict = @{@"nsData" : imgData,
};
[defaults setObject:dict forKey:key];
} else {
NSLog(@"NSExtensionItem Error, provider = %@", provider);
[self.extensionContext completeRequestReturningItems:@[] completionHandler:nil];
}
}
else {
NSDictionary *dict = @{@"name" : self.contentText};
[defaults setObject:dict forKey:key];
}
UIResponder *responder = self;
while (responder != nil) {
if ([responder respondsToSelector:@selector(openURL:)]) {
[responder performSelector:@selector(openURL:)
withObject:[NSURL URLWithString:@"message-linphone://" ]];
[self.extensionContext completeRequestReturningItems:@[] completionHandler:nil];
break;
}
responder = [responder nextResponder];
}
[defaults synchronize];
}];
}
@end

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.belledonne-communications.linphone</string>
</array>
</dict>
</plist>

1
submodules/externals/zxing-cpp vendored Submodule

@ -0,0 +1 @@
Subproject commit 8906fb7b243fa455fd9b091e4ac611536b2dbba4