Merge branch 'release/4.4'

This commit is contained in:
Danmei Chen 2021-03-23 16:12:56 +01:00
commit 8d10ccb3b1
44 changed files with 1037 additions and 1025 deletions

View file

@ -57,7 +57,8 @@ enum NetworkType: Int {
let paths = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true)
var writablePath = paths[0]
writablePath = writablePath.appending("/\(filePath)")
Log.directLog(BCTBX_LOG_MESSAGE, text: "file path is \(writablePath)")
let message:String = "file path is \(writablePath)"
Log.directLog(BCTBX_LOG_MESSAGE, text: message)
return writablePath
//file name is recording_contact-name_dayName-day-monthName-year-hour-minutes-seconds
//The recording prefix is used to identify recordings in the cache directory.

View file

@ -22,7 +22,7 @@
#import "TPKeyboardAvoidingScrollView.h"
#import "PhoneMainView.h"
@interface AssistantView : UIViewController <UITextFieldDelegate, UICompositeViewDelegate> {
@interface AssistantView : UIViewController <UITextFieldDelegate, UICompositeViewDelegate, UITextViewDelegate> {
@private
LinphoneAccountCreator *account_creator;
@ -33,6 +33,7 @@
size_t number_of_configs_before;
BOOL mustRestoreView;
long phone_number_length;
BOOL acceptTerms;
}
@property(nonatomic) UICompositeViewDescription *outgoingView;
@ -70,6 +71,10 @@
@property (weak, nonatomic) IBOutlet UIButton *downloadButton;
@property (weak, nonatomic) IBOutlet UITextField *urlLabel;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *createAccountNextButtonPositionConstraint;
@property (weak, nonatomic) IBOutlet UIButton *acceptButton;
- (IBAction)onAcceptTermsClick:(id)sender;
@property (weak, nonatomic) IBOutlet UITextView *acceptText;
+ (NSString *)StringForXMLRPCError:(const char *)err;
+ (NSString *)errorForLinphoneAccountCreatorPhoneNumberStatus:(LinphoneAccountCreatorPhoneNumberStatus)status;

View file

@ -62,6 +62,7 @@ typedef enum _ViewElement {
historyViews = [[NSMutableArray alloc] init];
currentView = nil;
mustRestoreView = NO;
acceptTerms = NO;
}
return self;
}
@ -119,12 +120,28 @@ static UICompositeViewDescription *compositeDescription = nil;
_outgoingView = DialerView.compositeViewDescription;
_qrCodeButton.hidden = !ENABLE_QRCODE;
[self resetLiblinphone:FALSE];
[self enableWelcomeViewButtons];
NSString *message = NSLocalizedString(@"I accept Belledonne Communications terms of use and privacy policy", nil);
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:message attributes:@{NSForegroundColorAttributeName : [UIColor systemGrayColor]}];
[attributedString addAttribute:NSLinkAttributeName
value:@"https://www.linphone.org/general-terms"
range:[[attributedString string] rangeOfString:NSLocalizedString(@"terms of use", nil)]];
[attributedString addAttribute:NSLinkAttributeName
value:@"https://www.linphone.org/privacy-policy"
range:[[attributedString string] rangeOfString:NSLocalizedString(@"privacy policy", nil)]];
NSDictionary *linkAttributes = @{NSForegroundColorAttributeName : [UIColor redColor],
NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle)};
_acceptText.linkTextAttributes = linkAttributes;
_acceptText.attributedText = attributedString;
_acceptText.editable = NO;
_acceptText.delegate = self;
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[NSNotificationCenter.defaultCenter removeObserver:self];
}
- (void)fitContent {
@ -143,6 +160,11 @@ static UICompositeViewDescription *compositeDescription = nil;
[self fitContent];
}
#pragma mark - UITextViewDelegate
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange interaction:(UITextItemInteraction)interaction {
return [[UIApplication sharedApplication] openURL:URL];
}
#pragma mark - Utils
- (void)resetLiblinphone:(BOOL)core {
@ -269,6 +291,12 @@ static UICompositeViewDescription *compositeDescription = nil;
return NSLocalizedString(@"Unknown error, please try again later.", nil);
}
- (void)enableWelcomeViewButtons {
UIImage *image = acceptTerms ? [UIImage imageNamed:@"checkbox_checked.png"] : [UIImage imageNamed:@"checkbox_unchecked.png"];
[_acceptButton setImage:image forState:UIControlStateNormal];
_gotoRemoteProvisioningButton.enabled = _gotoLinphoneLoginButton.enabled = _gotoCreateAccountButton.enabled = _gotoLoginButton.enabled = acceptTerms;
}
+ (NSString *)errorForLinphoneAccountCreatorPhoneNumberStatus:(LinphoneAccountCreatorPhoneNumberStatus)status {
switch (status) {
case LinphoneAccountCreatorPhoneNumberStatusTooShort: /**< Phone number too short */
@ -602,7 +630,7 @@ static UICompositeViewDescription *compositeDescription = nil;
[self changeView:_remoteProvisioningLoginView back:FALSE animation:TRUE];
linphone_proxy_config_destroy(default_conf);
linphone_proxy_config_unref(default_conf);
}
- (void)resetTextFields {
@ -690,10 +718,6 @@ static UICompositeViewDescription *compositeDescription = nil;
account_creator, inputEntry.length > 0 ? inputEntry.UTF8String : NULL, prefix.UTF8String);
if (s != LinphoneAccountCreatorPhoneNumberStatusOk) {
linphone_account_creator_set_phone_number(account_creator, NULL, NULL);
// if phone is empty and username is empty, this is wrong
if (linphone_account_creator_get_username(account_creator) == NULL) {
s = LinphoneAccountCreatorPhoneNumberStatusTooShort;
}
}
createPhone.errorLabel.text = [AssistantView errorForLinphoneAccountCreatorPhoneNumberStatus:s];
@ -1654,6 +1678,11 @@ void assistant_is_account_linked(LinphoneAccountCreator *creator, LinphoneAccoun
}
}
- (IBAction)onAcceptTermsClick:(id)sender {
acceptTerms = !acceptTerms;
[self enableWelcomeViewButtons];
}
#pragma mark - select country delegate
- (void)didSelectCountry:(NSDictionary *)country {

View file

@ -1,14 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="AssistantView">
<connections>
<outlet property="acceptButton" destination="tZu-nz-jXM" id="qiZ-p1-Fes"/>
<outlet property="acceptText" destination="R4J-fD-JSt" id="T8d-pv-nCc"/>
<outlet property="accountLabel" destination="vh7-65-1js" id="IU4-gv-v6m"/>
<outlet property="activationEmailText" destination="mbT-xm-bKk" id="fho-Ip-bTQ"/>
<outlet property="activationSMSText" destination="9AN-gT-I7D" id="GSl-Iw-JjR"/>
@ -42,7 +45,7 @@
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="WELCOME" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="126" 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="36"/>
<accessibility key="accessibilityConfiguration" label="Account setup assistant"/>
<fontDescription key="fontDescription" type="system" weight="light" pointSize="24"/>
<nil key="textColor"/>
@ -50,7 +53,7 @@
<size key="shadowOffset" width="-1" height="-1"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="This assistant will help you configure and use your SIP account." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="Yci-5h-O4o" userLabel="subtitleLabel">
<rect key="frame" x="36" y="66" width="303" height="58"/>
<rect key="frame" x="36" y="36" width="303" height="58"/>
<accessibility key="accessibilityConfiguration" label="Account setup assistant"/>
<constraints>
<constraint firstAttribute="height" constant="58" id="5LG-5A-dY3"/>
@ -60,8 +63,40 @@
<nil key="highlightedColor"/>
<size key="shadowOffset" width="-1" height="-1"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="plw-Mp-teD" userLabel="acceptView">
<rect key="frame" x="36" y="94" width="303" height="58"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="tZu-nz-jXM" userLabel="acceptButton">
<rect key="frame" x="20" y="2" width="54" height="54"/>
<constraints>
<constraint firstAttribute="width" constant="54" id="4WQ-mk-JlN"/>
</constraints>
<state key="normal" image="checkbox_unchecked.png"/>
<connections>
<action selector="onAcceptTermsClick:" destination="-1" eventType="touchUpInside" id="grZ-W7-Lyz"/>
</connections>
</button>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" editable="NO" text="I accept Belledonne Communications terms of use and privacy policy" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="R4J-fD-JSt" userLabel="acceptText">
<rect key="frame" x="68" y="0.0" width="235" height="58"/>
<color key="textColor" systemColor="systemGrayColor"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
</subviews>
<constraints>
<constraint firstItem="tZu-nz-jXM" firstAttribute="top" secondItem="plw-Mp-teD" secondAttribute="top" constant="2" id="34h-dq-qBh"/>
<constraint firstAttribute="bottom" secondItem="R4J-fD-JSt" secondAttribute="bottom" id="P1m-ak-sM2"/>
<constraint firstItem="R4J-fD-JSt" firstAttribute="top" secondItem="plw-Mp-teD" secondAttribute="top" id="XMu-hP-ZJe"/>
<constraint firstItem="R4J-fD-JSt" firstAttribute="leading" secondItem="plw-Mp-teD" secondAttribute="leading" constant="68" id="dSo-Mw-hmv"/>
<constraint firstAttribute="trailing" secondItem="R4J-fD-JSt" secondAttribute="trailing" id="hcC-zD-4QJ"/>
<constraint firstItem="tZu-nz-jXM" firstAttribute="leading" secondItem="plw-Mp-teD" secondAttribute="leading" constant="20" id="rOn-Of-kjI"/>
<constraint firstAttribute="bottom" secondItem="tZu-nz-jXM" secondAttribute="bottom" constant="2" id="tzO-fa-ite"/>
<constraint firstAttribute="height" constant="58" id="zjY-mm-MaQ"/>
</constraints>
</view>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="wordWrap" translatesAutoresizingMaskIntoConstraints="NO" id="36" userLabel="createAccountButton" customClass="UIRoundBorderedButton">
<rect key="frame" x="40" y="153" width="299" height="40"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<accessibility key="accessibilityConfiguration" label="Create account">
<bool key="isElement" value="YES"/>
</accessibility>
@ -70,7 +105,10 @@
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<state key="normal" title="CREATE ACCOUNT">
<color key="titleColor" systemColor="labelColor" cocoaTouchSystemColor="darkTextColor"/>
<color key="titleColor" systemColor="labelColor"/>
</state>
<state key="disabled">
<color key="titleColor" systemColor="systemGray2Color"/>
</state>
<state key="highlighted" backgroundImage="color_F.png"/>
<connections>
@ -80,14 +118,17 @@
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="wordWrap" translatesAutoresizingMaskIntoConstraints="NO" id="38" userLabel="linphoneLoginButton" customClass="UIRoundBorderedButton">
<rect key="frame" x="40" y="227" width="299" height="40"/>
<accessibility key="accessibilityConfiguration" label="Use Linphone account">
<bool key="isElement" value="YES"/>
<bool key="isElement" value="NO"/>
</accessibility>
<constraints>
<constraint firstAttribute="height" constant="40" id="NkG-qe-o3R"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<state key="normal" title="USE LINPHONE ACCOUNT">
<color key="titleColor" systemColor="labelColor" cocoaTouchSystemColor="darkTextColor"/>
<color key="titleColor" systemColor="labelColor"/>
</state>
<state key="disabled">
<color key="titleColor" systemColor="systemGray2Color"/>
</state>
<state key="highlighted" backgroundImage="color_F.png">
<color key="titleColor" red="0.67030966281890869" green="0.71867996454238892" blue="0.75078284740447998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@ -106,7 +147,10 @@
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<state key="normal" title="USE SIP ACCOUNT">
<color key="titleColor" systemColor="labelColor" cocoaTouchSystemColor="darkTextColor"/>
<color key="titleColor" systemColor="labelColor"/>
</state>
<state key="disabled">
<color key="titleColor" systemColor="systemGray2Color"/>
</state>
<state key="highlighted" backgroundImage="color_F.png">
<color key="titleColor" red="0.67030966281890869" green="0.71867996454238892" blue="0.75078284740447998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@ -125,7 +169,10 @@
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<state key="normal" title="FETCH REMOTE CONFIGURATION">
<color key="titleColor" systemColor="labelColor" cocoaTouchSystemColor="darkTextColor"/>
<color key="titleColor" systemColor="labelColor"/>
</state>
<state key="disabled">
<color key="titleColor" systemColor="systemGray2Color"/>
</state>
<state key="highlighted" backgroundImage="color_F.png">
<color key="titleColor" red="0.67030966281890869" green="0.71867996454238892" blue="0.75078284740447998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@ -138,13 +185,16 @@
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="38" firstAttribute="leading" secondItem="39" secondAttribute="leading" id="23x-IR-Mtg"/>
<constraint firstItem="plw-Mp-teD" firstAttribute="top" secondItem="Yci-5h-O4o" secondAttribute="bottom" id="94M-05-soq"/>
<constraint firstItem="39" firstAttribute="top" secondItem="38" secondAttribute="bottom" constant="34" id="CyI-RI-Fpo"/>
<constraint firstItem="126" firstAttribute="top" secondItem="33" secondAttribute="top" id="Dck-Yc-Nhv"/>
<constraint firstItem="39" firstAttribute="leading" secondItem="Kbn-dL-C5h" secondAttribute="leading" id="JTU-gj-eeY"/>
<constraint firstItem="126" firstAttribute="leading" secondItem="33" secondAttribute="leading" constant="36" id="Mwl-ls-3tB"/>
<constraint firstAttribute="bottom" secondItem="Kbn-dL-C5h" secondAttribute="bottom" constant="10" id="PWn-9N-k5j"/>
<constraint firstItem="plw-Mp-teD" firstAttribute="top" secondItem="Yci-5h-O4o" secondAttribute="bottom" id="RUw-sM-ywE"/>
<constraint firstItem="36" firstAttribute="leading" secondItem="38" secondAttribute="leading" id="Yd2-4o-VP3"/>
<constraint firstItem="36" firstAttribute="top" secondItem="Yci-5h-O4o" secondAttribute="bottom" constant="29" id="awh-hc-hk8"/>
<constraint firstItem="36" firstAttribute="top" secondItem="plw-Mp-teD" secondAttribute="bottom" constant="1" id="b8b-dc-3cx"/>
<constraint firstItem="plw-Mp-teD" firstAttribute="leading" secondItem="33" secondAttribute="leading" constant="36" id="caQ-q5-EyX"/>
<constraint firstItem="38" firstAttribute="top" secondItem="36" secondAttribute="bottom" constant="34" id="drU-e1-uCL"/>
<constraint firstItem="36" firstAttribute="trailing" secondItem="38" secondAttribute="trailing" id="gNw-r2-XIi"/>
<constraint firstItem="39" firstAttribute="trailing" secondItem="Kbn-dL-C5h" secondAttribute="trailing" id="gpT-pW-S1w"/>
@ -153,13 +203,14 @@
<constraint firstItem="36" firstAttribute="leading" secondItem="33" secondAttribute="leading" constant="40" id="nWj-20-FOd"/>
<constraint firstItem="Kbn-dL-C5h" firstAttribute="top" secondItem="39" secondAttribute="bottom" constant="35" id="niQ-BM-5yR"/>
<constraint firstAttribute="trailing" secondItem="36" secondAttribute="trailing" constant="36" id="uIa-qF-c8m"/>
<constraint firstItem="Yci-5h-O4o" firstAttribute="top" secondItem="126" secondAttribute="bottom" constant="7" id="v51-4n-Jq1"/>
<constraint firstItem="Yci-5h-O4o" firstAttribute="top" secondItem="126" secondAttribute="bottom" id="v51-4n-Jq1"/>
<constraint firstAttribute="trailing" secondItem="plw-Mp-teD" secondAttribute="trailing" constant="36" id="wSJ-2t-lWy"/>
<constraint firstItem="126" firstAttribute="centerX" secondItem="33" secondAttribute="centerX" id="z6G-VJ-BQy"/>
<constraint firstItem="38" firstAttribute="trailing" secondItem="39" secondAttribute="trailing" id="z7W-lC-fXR"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="203.5" y="189"/>
<point key="canvasLocation" x="202.40000000000001" y="188.90554722638683"/>
</view>
<view contentMode="scaleToFill" id="44" userLabel="createAccountView">
<rect key="frame" x="0.0" y="0.0" width="375" height="739"/>
@ -201,7 +252,7 @@
<constraint firstAttribute="height" constant="30" id="pz8-ke-WQV"/>
</constraints>
<state key="normal" title="Select your country">
<color key="titleColor" systemColor="labelColor" cocoaTouchSystemColor="darkTextColor"/>
<color key="titleColor" systemColor="labelColor"/>
</state>
<state key="highlighted" title="Choose your country" backgroundImage="color_F.png"/>
<connections>
@ -210,7 +261,7 @@
</button>
<textField opaque="NO" clipsSubviews="YES" tag="109" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="+1" textAlignment="center" minimumFontSize="5" translatesAutoresizingMaskIntoConstraints="NO" id="aG8-DH-06s" userLabel="countryCodeField" customClass="UIAssistantTextField">
<rect key="frame" x="38" y="190" width="54" height="30"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" keyboardType="phonePad"/>
<connections>
@ -221,7 +272,7 @@
</textField>
<textField opaque="NO" clipsSubviews="YES" tag="107" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" adjustsFontSizeToFit="NO" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="74" userLabel="phoneField" customClass="UIAssistantTextField">
<rect key="frame" x="95" y="190" width="242" height="30"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor"/>
<accessibility key="accessibilityConfiguration" label="Username"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" keyboardType="phonePad" returnKeyType="next"/>
@ -293,7 +344,7 @@
</label>
<textField opaque="NO" clipsSubviews="YES" tag="100" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" adjustsFontSizeToFit="NO" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="OLm-Nz-ouj" userLabel="usernameField" customClass="UIAssistantTextField">
<rect key="frame" x="38" y="23" width="299" height="34"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor"/>
<accessibility key="accessibilityConfiguration" label="Username"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" returnKeyType="next"/>
@ -344,7 +395,7 @@
</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="291" height="34"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor"/>
<accessibility key="accessibilityConfiguration" label="Email"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" keyboardType="emailAddress" returnKeyType="done"/>
@ -373,7 +424,7 @@
</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="105" width="291" height="34"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor"/>
<accessibility key="accessibilityConfiguration" label="Password "/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" returnKeyType="next" secureTextEntry="YES"/>
@ -402,7 +453,7 @@
</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="183" width="291" height="34"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor"/>
<accessibility key="accessibilityConfiguration" label="Password confirmation"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" returnKeyType="next" secureTextEntry="YES"/>
@ -458,7 +509,7 @@
</accessibility>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<state key="normal" title="CREATE ACCOUNT">
<color key="titleColor" systemColor="labelColor" cocoaTouchSystemColor="darkTextColor"/>
<color key="titleColor" systemColor="labelColor"/>
</state>
<state key="disabled">
<color key="titleColor" red="0.71885228157043457" green="0.71883076429367065" blue="0.71884298324584961" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@ -586,7 +637,7 @@
</label>
<textField opaque="NO" clipsSubviews="YES" tag="108" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Activation code" textAlignment="center" minimumFontSize="17" clearButtonMode="always" translatesAutoresizingMaskIntoConstraints="NO" id="ULe-kp-qZn">
<rect key="frame" x="38" y="194" width="299" height="71"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor"/>
<fontDescription key="fontDescription" type="system" pointSize="26"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no" keyboardType="numberPad" returnKeyType="done"/>
</textField>
@ -600,7 +651,7 @@
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<state key="normal" title="ACTIVATE YOUR ACCOUNT">
<color key="titleColor" systemColor="labelColor" cocoaTouchSystemColor="darkTextColor"/>
<color key="titleColor" systemColor="labelColor"/>
</state>
<state key="disabled">
<color key="titleColor" red="0.71885228157043457" green="0.71883076429367065" blue="0.71884298324584961" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@ -692,7 +743,7 @@ Once it is done, come back here and click on the button.</string>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<state key="normal" title="FINISH CONFIGURATION">
<color key="titleColor" systemColor="labelColor" cocoaTouchSystemColor="darkTextColor"/>
<color key="titleColor" systemColor="labelColor"/>
</state>
<state key="disabled">
<color key="titleColor" red="0.71885228157043457" green="0.71883076429367065" blue="0.71884298324584961" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@ -756,7 +807,7 @@ Once it is done, come back here and click on the button.</string>
<constraint firstAttribute="height" constant="30" id="X9C-Ll-LuK"/>
</constraints>
<state key="normal" title="Select your country">
<color key="titleColor" systemColor="labelColor" cocoaTouchSystemColor="darkTextColor"/>
<color key="titleColor" systemColor="labelColor"/>
</state>
<state key="highlighted" title="Choose your country" backgroundImage="color_F.png"/>
<connections>
@ -765,7 +816,7 @@ Once it is done, come back here and click on the button.</string>
</button>
<textField opaque="NO" clipsSubviews="YES" tag="109" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="+1" textAlignment="center" minimumFontSize="5" translatesAutoresizingMaskIntoConstraints="NO" id="iEA-YK-kua" userLabel="countryCodeField" customClass="UIAssistantTextField">
<rect key="frame" x="38" y="179" width="54" height="30"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" keyboardType="phonePad"/>
<connections>
@ -776,7 +827,7 @@ Once it is done, come back here and click on the button.</string>
</textField>
<textField opaque="NO" clipsSubviews="YES" tag="107" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" adjustsFontSizeToFit="NO" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="Pcj-jL-x9P" userLabel="phoneField" customClass="UIAssistantTextField">
<rect key="frame" x="95" y="179" width="242" height="30"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor"/>
<accessibility key="accessibilityConfiguration" label="Username"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" keyboardType="phonePad" returnKeyType="next"/>
@ -824,7 +875,7 @@ Once it is done, come back here and click on the button.</string>
</label>
<textField opaque="NO" clipsSubviews="YES" tag="100" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" adjustsFontSizeToFit="NO" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="YRW-ex-VZy" userLabel="usernameField" customClass="UIAssistantTextField">
<rect key="frame" x="38" y="20" width="299" height="30"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor"/>
<accessibility key="accessibilityConfiguration" label="Username"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="QcL-i5-g1D"/>
@ -857,7 +908,7 @@ Once it is done, come back here and click on the button.</string>
</label>
<textField opaque="NO" clipsSubviews="YES" tag="101" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" adjustsFontSizeToFit="NO" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="ap4-xh-CVK" userLabel="passwordField" customClass="UIAssistantTextField">
<rect key="frame" x="38" y="93" width="299" height="30"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor"/>
<accessibility key="accessibilityConfiguration" label="Password"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="BWP-n1-3OS"/>
@ -882,7 +933,7 @@ Once it is done, come back here and click on the button.</string>
</connections>
</label>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<accessibility key="accessibilityConfiguration">
<accessibilityTraits key="traits" notEnabled="YES"/>
</accessibility>
@ -928,7 +979,7 @@ Once it is done, come back here and click on the button.</string>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<state key="normal" title="LOGIN">
<color key="titleColor" systemColor="labelColor" cocoaTouchSystemColor="darkTextColor"/>
<color key="titleColor" systemColor="labelColor"/>
</state>
<state key="disabled">
<color key="titleColor" red="0.71885228157043457" green="0.71883076429367065" blue="0.71884298324584961" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@ -1033,7 +1084,7 @@ Once it is done, come back here and click on the button.</string>
</label>
<textField opaque="NO" clipsSubviews="YES" tag="100" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" adjustsFontSizeToFit="NO" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="FJ1-Xt-g7g" userLabel="usernameField" customClass="UIAssistantTextField">
<rect key="frame" x="38" y="153" width="298" height="30"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor"/>
<accessibility key="accessibilityConfiguration" label="Username"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="Xxt-U5-krh"/>
@ -1066,7 +1117,7 @@ Once it is done, come back here and click on the button.</string>
</label>
<textField opaque="NO" clipsSubviews="YES" tag="101" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" adjustsFontSizeToFit="NO" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="zEa-Dj-QiH" userLabel="passwordField" customClass="UIAssistantTextField">
<rect key="frame" x="38" y="227" width="298" height="30"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor"/>
<accessibility key="accessibilityConfiguration" label="Password"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="ywR-ZJ-1YN"/>
@ -1099,7 +1150,7 @@ Once it is done, come back here and click on the button.</string>
</label>
<textField opaque="NO" clipsSubviews="YES" tag="104" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" adjustsFontSizeToFit="NO" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="5kh-Wo-SMY" userLabel="domainField" customClass="UIAssistantTextField">
<rect key="frame" x="38" y="301" width="298" height="30"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor"/>
<accessibility key="accessibilityConfiguration" label="Domain"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="Evx-tD-kkq"/>
@ -1153,7 +1204,7 @@ Once it is done, come back here and click on the button.</string>
</label>
<textField opaque="NO" clipsSubviews="YES" tag="106" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" adjustsFontSizeToFit="NO" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="7Cb-fa-CY5" userLabel="displayNameField" customClass="UIAssistantTextField">
<rect key="frame" x="38" y="375" width="298" height="30"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor"/>
<accessibility key="accessibilityConfiguration" label="Display name"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="Mr8-Nc-w1K"/>
@ -1184,7 +1235,7 @@ Once it is done, come back here and click on the button.</string>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<state key="normal" title="LOGIN">
<color key="titleColor" systemColor="labelColor" cocoaTouchSystemColor="darkTextColor"/>
<color key="titleColor" systemColor="labelColor"/>
</state>
<state key="disabled">
<color key="titleColor" red="0.71885228157043457" green="0.71883076429367065" blue="0.71884298324584961" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@ -1288,7 +1339,7 @@ Once it is done, come back here and click on the button.</string>
</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="123" width="299" height="30"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor"/>
<accessibility key="accessibilityConfiguration" label="URL"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="7OB-M5-kU2"/>
@ -1319,7 +1370,7 @@ Once it is done, come back here and click on the button.</string>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<state key="normal" title="FETCH AND APPLY">
<color key="titleColor" systemColor="labelColor" cocoaTouchSystemColor="darkTextColor"/>
<color key="titleColor" systemColor="labelColor"/>
</state>
<state key="disabled">
<color key="titleColor" red="0.71885228157043457" green="0.71883076429367065" blue="0.71884298324584961" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@ -1339,7 +1390,7 @@ Once it is done, come back here and click on the button.</string>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<state key="normal" title="QR CODE">
<color key="titleColor" systemColor="labelColor" cocoaTouchSystemColor="darkTextColor"/>
<color key="titleColor" systemColor="labelColor"/>
</state>
<state key="disabled">
<color key="titleColor" red="0.71885228160000003" green="0.71883076430000004" blue="0.71884298319999995" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@ -1412,7 +1463,7 @@ Once it is done, come back here and click on the button.</string>
</label>
<textField opaque="NO" clipsSubviews="YES" tag="100" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" adjustsFontSizeToFit="NO" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="qgP-7X-pUs" userLabel="usernameField" customClass="UIAssistantTextField">
<rect key="frame" x="38" y="153" width="299" height="30"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor"/>
<accessibility key="accessibilityConfiguration" label="Username"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="f5B-bE-Uf0"/>
@ -1435,7 +1486,7 @@ Once it is done, come back here and click on the button.</string>
</label>
<textField opaque="NO" clipsSubviews="YES" tag="101" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" adjustsFontSizeToFit="NO" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="hfJ-yu-wVn" userLabel="passwordField" customClass="UIAssistantTextField">
<rect key="frame" x="38" y="217" width="299" height="30"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor"/>
<accessibility key="accessibilityConfiguration" label="Password"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="cfl-mf-gkE"/>
@ -1458,7 +1509,7 @@ Once it is done, come back here and click on the button.</string>
</label>
<textField opaque="NO" clipsSubviews="YES" tag="104" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" adjustsFontSizeToFit="NO" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="gfr-KY-4vo" userLabel="domainField" customClass="UIAssistantTextField">
<rect key="frame" x="38" y="281" width="299" height="30"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor"/>
<accessibility key="accessibilityConfiguration" label="Domain"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="RNp-Me-1Fq"/>
@ -1480,7 +1531,7 @@ Once it is done, come back here and click on the button.</string>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<state key="normal" title="LOGIN">
<color key="titleColor" systemColor="labelColor" cocoaTouchSystemColor="darkTextColor"/>
<color key="titleColor" systemColor="labelColor"/>
</state>
<state key="disabled">
<color key="titleColor" red="0.71885228157043457" green="0.71883076429367065" blue="0.71884298324584961" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@ -1539,6 +1590,22 @@ Once it is done, come back here and click on the button.</string>
</view>
</objects>
<resources>
<image name="checkbox_unchecked.png" width="27.200000762939453" height="27.200000762939453"/>
<image name="color_F.png" width="2" height="2"/>
<systemColor name="labelColor">
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
<systemColor name="secondarySystemBackgroundColor">
<color red="0.94901960784313721" green="0.94901960784313721" blue="0.96862745098039216" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
<systemColor name="systemGray2Color">
<color red="0.68235294117647061" green="0.68235294117647061" blue="0.69803921568627447" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
<systemColor name="systemGrayColor">
<color red="0.55686274509803924" green="0.55686274509803924" blue="0.57647058823529407" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
</resources>
</document>

View file

@ -1,9 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@ -45,7 +46,7 @@
<rect key="frame" x="0.0" y="0.0" width="540" height="66"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<subviews>
<textField opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Enter a number or an address" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" minimumFontSize="5" translatesAutoresizingMaskIntoConstraints="NO" id="4" userLabel="addressField" customClass="UIAddressTextField">
<textField opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Enter a number or an address" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" minimumFontSize="15" translatesAutoresizingMaskIntoConstraints="NO" id="4" userLabel="addressField" customClass="UIAddressTextField">
<rect key="frame" x="31" y="0.0" width="426" height="66"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
@ -70,7 +71,7 @@
</connections>
</button>
</subviews>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor"/>
</view>
<imageView userInteractionEnabled="NO" tag="2" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="dialer_background.png" translatesAutoresizingMaskIntoConstraints="NO" id="AH0-S5-0Ku" userLabel="logoImage">
<rect key="frame" x="100" y="279" width="340" height="294"/>
@ -238,7 +239,7 @@
</connections>
</button>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
</view>
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="182" userLabel="bottomBar">
<rect key="frame" x="0.0" y="786" width="540" height="66"/>
@ -287,7 +288,7 @@
</connections>
</button>
</subviews>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor"/>
</view>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@ -338,5 +339,11 @@
<image name="numpad_over_background.png" width="2" height="2"/>
<image name="numpad_star_default.png" width="84.800003051757812" height="81.599998474121094"/>
<image name="numpad_star_over.png" width="84.800003051757812" height="81.599998474121094"/>
<systemColor name="secondarySystemBackgroundColor">
<color red="0.94901960784313721" green="0.94901960784313721" blue="0.96862745098039216" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>

View file

@ -33,11 +33,10 @@ import AVFoundation
* CallManager is a class that manages application calls and supports callkit.
* There is only one CallManager by calling CallManager.instance().
*/
@objc class CallManager: NSObject {
@objc class CallManager: NSObject, CoreDelegate {
static var theCallManager: CallManager?
let providerDelegate: ProviderDelegate! // to support callkit
let callController: CXCallController! // to support callkit
let manager: CoreManagerDelegate! // callbacks of the linphonecore
var lc: Core?
@objc var speakerBeforePause : Bool = false
@objc var speakerEnabled : Bool = false
@ -47,12 +46,14 @@ import AVFoundation
var referedFromCall: String?
var referedToCall: String?
var endCallkit: Bool = false
static var speaker_already_enabled : Bool = false
var globalState : GlobalState = .Off
var actionsToPerformOnceWhenCoreIsOn : [(()->Void)] = []
fileprivate override init() {
providerDelegate = ProviderDelegate()
callController = CXCallController()
manager = CoreManagerDelegate()
}
@objc static func instance() -> CallManager {
@ -64,7 +65,7 @@ import AVFoundation
@objc func setCore(core: OpaquePointer) {
lc = Core.getSwiftObject(cObject: core)
lc?.addDelegate(delegate: manager)
lc?.addDelegate(delegate: self)
}
@objc static func getAppData(call: OpaquePointer) -> CallAppData? {
@ -275,7 +276,7 @@ import AVFoundation
if (CallManager.instance().nextCallIsTransfer) {
let call = CallManager.instance().lc!.currentCall
try call?.transfer(referTo: addr.asString())
try call?.transferTo(referTo: addr)
CallManager.instance().nextCallIsTransfer = false
} else {
//We set the record file name here because we can't do it after the call is started.
@ -331,18 +332,6 @@ import AVFoundation
providerDelegate.uuids.removeAll()
}
// To be removed.
static func configAudioSession(audioSession: AVAudioSession) {
do {
try audioSession.setCategory(AVAudioSession.Category.playAndRecord, mode: AVAudioSession.Mode.voiceChat, options: AVAudioSession.CategoryOptions(rawValue: AVAudioSession.CategoryOptions.allowBluetooth.rawValue | AVAudioSession.CategoryOptions.allowBluetoothA2DP.rawValue))
try audioSession.setMode(AVAudioSession.Mode.voiceChat)
try audioSession.setPreferredSampleRate(48000.0)
try AVAudioSession.sharedInstance().setActive(true, options: [])
} catch {
Log.directLog(BCTBX_LOG_WARNING, text: "CallKit: Unable to config audio session because : \(error)")
}
}
@objc func terminateCall(call: OpaquePointer?) {
if (call == nil) {
Log.directLog(BCTBX_LOG_ERROR, text: "Can not terminate null call!")
@ -355,9 +344,6 @@ import AVFoundation
} catch {
Log.directLog(BCTBX_LOG_ERROR, text: "Failed to terminate call failed because \(error)")
}
if (UIApplication.shared.applicationState == .background) {
CoreManager.instance().stopLinphoneCore()
}
}
@objc func markCallAsDeclined(callId: String) {
@ -409,32 +395,32 @@ import AVFoundation
}
@objc func performActionWhenCoreIsOn(action: @escaping ()->Void ) {
if (manager.globalState == .On) {
if (globalState == .On) {
action()
} else {
manager.actionsToPerformOnceWhenCoreIsOn.append(action)
actionsToPerformOnceWhenCoreIsOn.append(action)
}
}
}
class CoreManagerDelegate: CoreDelegate {
static var speaker_already_enabled : Bool = false
var globalState : GlobalState = .Off
var actionsToPerformOnceWhenCoreIsOn : [(()->Void)] = []
override func onGlobalStateChanged(lc: Core, gstate: GlobalState, message: String) {
if (gstate == .On) {
@objc func acceptVideo(call: OpaquePointer, confirm: Bool) {
let sCall = Call.getSwiftObject(cObject: call)
let params = try? lc?.createCallParams(call: sCall)
params?.videoEnabled = confirm
try? sCall.acceptUpdate(params: params)
}
func onGlobalStateChanged(core: Core, state: GlobalState, message: String) {
if (state == .On) {
actionsToPerformOnceWhenCoreIsOn.forEach {
$0()
}
actionsToPerformOnceWhenCoreIsOn.removeAll()
}
globalState = gstate
globalState = state
}
override func onRegistrationStateChanged(lc: Core, cfg: ProxyConfig, cstate: RegistrationState, message: String) {
if lc.proxyConfigList.count == 1 && (cstate == .Failed || cstate == .Cleared){
func onRegistrationStateChanged(core: Core, proxyConfig: ProxyConfig, state: RegistrationState, message: String) {
if core.proxyConfigList.count == 1 && (state == .Failed || state == .Cleared){
// terminate callkit immediately when registration failed or cleared, supporting single proxy configuration
CallManager.instance().endCallkit = true
for call in CallManager.instance().providerDelegate.uuids {
@ -445,15 +431,15 @@ class CoreManagerDelegate: CoreDelegate {
}
}
override func onCallStateChanged(lc: Core, call: Call, cstate: Call.State, message: String) {
func onCallStateChanged(core: Core, call: Call, state cstate: Call.State, message: String) {
let addr = call.remoteAddress;
let displayName = FastAddressBook.displayName(for: addr?.getCobject) ?? "Unknow"
let callLog = call.callLog
let callId = callLog?.callId
let video = UIApplication.shared.applicationState == .active && (lc.videoActivationPolicy?.automaticallyAccept ?? false) && (call.remoteParams?.videoEnabled ?? false)
let video = UIApplication.shared.applicationState == .active && (core.videoActivationPolicy?.automaticallyAccept ?? false) && (call.remoteParams?.videoEnabled ?? false)
// we keep the speaker auto-enabled state in this static so that we don't
// force-enable it on ICE re-invite if the user disabled it.
CoreManagerDelegate.speaker_already_enabled = false
CallManager.speaker_already_enabled = false
if (call.userData == nil) {
let appData = CallAppData()
@ -507,7 +493,7 @@ class CoreManagerDelegate: CoreDelegate {
if (CallManager.instance().speakerBeforePause) {
CallManager.instance().speakerBeforePause = false
CallManager.instance().enableSpeaker(enable: true)
CoreManagerDelegate.speaker_already_enabled = true
CallManager.speaker_already_enabled = true
}
break
case .OutgoingInit,
@ -533,7 +519,7 @@ class CoreManagerDelegate: CoreDelegate {
case .End,
.Error:
UIDevice.current.isProximityMonitoringEnabled = false
CoreManagerDelegate.speaker_already_enabled = false
CallManager.speaker_already_enabled = false
if (CallManager.instance().lc!.callsNb == 0) {
CallManager.instance().enableSpeaker(enable: false)
// disable this because I don't find anygood reason for it: _bluetoothAvailable = FALSE;
@ -600,9 +586,9 @@ class CoreManagerDelegate: CoreDelegate {
}
if (cstate == .IncomingReceived || cstate == .OutgoingInit || cstate == .Connected || cstate == .StreamsRunning) {
if ((call.currentParams?.videoEnabled ?? false) && !CoreManagerDelegate.speaker_already_enabled && !CallManager.instance().bluetoothEnabled) {
if ((call.currentParams?.videoEnabled ?? false) && !CallManager.speaker_already_enabled && !CallManager.instance().bluetoothEnabled) {
CallManager.instance().enableSpeaker(enable: true)
CoreManagerDelegate.speaker_already_enabled = true
CallManager.speaker_already_enabled = true
}
}

View file

@ -685,9 +685,7 @@ static void hideSpinner(LinphoneCall *call, void *user_data) {
onCancelClick:^() {
LOGI(@"User declined video proposal");
if (call == linphone_core_get_current_call(LC)) {
LinphoneCallParams *params = linphone_core_create_call_params(LC, call);
linphone_call_accept_update(call, params);
linphone_call_params_destroy(params);
[CallManager.instance acceptVideoWithCall:call confirm:FALSE];
[videoDismissTimer invalidate];
videoDismissTimer = nil;
}
@ -695,10 +693,7 @@ static void hideSpinner(LinphoneCall *call, void *user_data) {
onConfirmationClick:^() {
LOGI(@"User accept video proposal");
if (call == linphone_core_get_current_call(LC)) {
LinphoneCallParams *params = linphone_core_create_call_params(LC, call);
linphone_call_params_enable_video(params, TRUE);
linphone_call_accept_update(call, params);
linphone_call_params_destroy(params);
[CallManager.instance acceptVideoWithCall:call confirm:TRUE];
[videoDismissTimer invalidate];
videoDismissTimer = nil;
}

View file

@ -49,4 +49,6 @@
- (IBAction)onBackClick:(id)sender;
- (IBAction)onQuitClick:(id)sender;
- (void)removeCallbacks;
@end

View file

@ -132,6 +132,10 @@ static UICompositeViewDescription *compositeDescription = nil;
}
- (void)viewWillDisappear:(BOOL)animated {
[self removeCallbacks];
}
- (void)removeCallbacks {
if (!_room || !_chatRoomCbs)
return;
@ -299,7 +303,7 @@ static UICompositeViewDescription *compositeDescription = nil;
}
cell.uri = _contacts[indexPath.row];
LinphoneAddress *addr = linphone_address_new(cell.uri.UTF8String);
cell.nameLabel.text = [FastAddressBook displayNameForAddress:addr];
cell.nameLabel.text = (addr == nil? cell.uri : [FastAddressBook displayNameForAddress:addr]);
[cell.avatarImage setImage:[FastAddressBook imageForAddress:addr] bordered:YES withRoundedRadius:YES];
cell.controllerView = self;
if(![_admins containsObject:cell.uri]) {

View file

@ -27,8 +27,8 @@
@protocol ChatConversationDelegate <NSObject>
- (BOOL)startImageUpload:(UIImage *)image assetId:(NSString *)phAssetId withQuality:(float)quality;
- (BOOL)startFileUpload:(NSData *)data assetId:(NSString *)phAssetId;
- (BOOL)resendFile:(NSData *)data withName:(NSString *)name type:(NSString *)type key:(NSString *)key message:(NSString *)message;
- (BOOL)startImageUpload:(UIImage *)image withQuality:(float)quality andMessage:(NSString *)message;
- (BOOL)startFileUpload:(NSData *)data withName:(NSString *)name;
- (void)resendChat:(NSString *)message withExternalUrl:(NSString *)url;
- (void)tableViewIsScrolling;

View file

@ -193,12 +193,11 @@ static const int BASIC_EVENT_LIST=15;
- (BOOL)isFirstIndexInTableView:(NSIndexPath *)indexPath chat:(LinphoneChatMessage *)chat {
LinphoneEventLog *previousEvent = nil;
NSInteger indexOfPreviousEvent = indexPath.row - 1;
while (!previousEvent && indexOfPreviousEvent > -1) {
LinphoneEventLog *tmp = [[eventList objectAtIndex:indexOfPreviousEvent] pointerValue];
if (linphone_event_log_get_type(tmp) == LinphoneEventLogTypeConferenceChatMessage) {
previousEvent = tmp;
if (indexOfPreviousEvent > -1) {
previousEvent = [[eventList objectAtIndex:indexOfPreviousEvent] pointerValue];
if (linphone_event_log_get_type(previousEvent) != LinphoneEventLogTypeConferenceChatMessage) {
return TRUE;
}
--indexOfPreviousEvent;
}
if (!previousEvent)
return TRUE;
@ -256,11 +255,11 @@ static const int BASIC_EVENT_LIST=15;
kCellId = NSStringFromClass(UIChatBubblePhotoCell.class);
else
kCellId = NSStringFromClass(UIChatBubbleTextCell.class);
// To use less memory and to avoid overlapping. To be improved.
UIChatBubbleTextCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellId];
if (!cell) {
cell = [[NSClassFromString(kCellId) alloc] initWithIdentifier:kCellId];
}
cell = [[NSClassFromString(kCellId) alloc] initWithIdentifier:kCellId];
[cell setEvent:event];
if (chat) {
cell.isFirst = [self isFirstIndexInTableView:indexPath chat:chat];
@ -363,9 +362,9 @@ static const CGFloat MESSAGE_SPACING_PERCENTAGE = 1.f;
- (void)removeSelectionUsing:(void (^)(NSIndexPath *))remover {
[super removeSelectionUsing:^(NSIndexPath *indexPath) {
LinphoneEventLog *event = [[eventList objectAtIndex:indexPath.row] pointerValue];
// TODO: fix workaround
//linphone_event_log_delete_from_database(event);
linphone_chat_room_delete_message(_chatRoom, linphone_event_log_get_chat_message(event));
if (linphone_event_log_get_chat_message(event)) {
linphone_chat_room_delete_message(_chatRoom, linphone_event_log_get_chat_message(event));
}
NSInteger index = indexPath.row + _currentIndex + (totalEventList.count - eventList.count);
if (index < totalEventList.count)
[totalEventList removeObjectAtIndex:index];

View file

@ -92,6 +92,11 @@
+ (void)markAsRead:(LinphoneChatRoom *)chatRoom;
+ (void)autoDownload:(LinphoneChatMessage *)message;
+(NSString *)getKeyFromFileType:(NSString *)fileType fileName:(NSString *)name;
+ (NSURL *)getCacheFileUrl:(NSString *)name;
+ (void)writeFileInCache:(NSData *)data name:(NSString *)name;
+ (NSData *)getCacheFileData:(NSString *)name;
+ (void)writeMediaToGallery:(NSString *)name fileType:(NSString *)fileType;
- (void)configureForRoom:(BOOL)editing;
- (IBAction)onBackClick:(id)event;

View file

@ -253,7 +253,7 @@ static UICompositeViewDescription *compositeDescription = nil;
linphone_chat_room_cbs_set_participant_removed(_chatRoomCbs, on_chat_room_participant_removed);
linphone_chat_room_cbs_set_participant_admin_status_changed(_chatRoomCbs, on_chat_room_participant_admin_status_changed);
linphone_chat_room_cbs_set_chat_message_received(_chatRoomCbs, on_chat_room_chat_message_received);
linphone_chat_room_cbs_set_chat_message_sent(_chatRoomCbs, on_chat_room_chat_message_sent);
linphone_chat_room_cbs_set_chat_message_sending(_chatRoomCbs, on_chat_room_chat_message_sending);
linphone_chat_room_cbs_set_is_composing_received(_chatRoomCbs, on_chat_room_is_composing_received);
linphone_chat_room_cbs_set_conference_joined(_chatRoomCbs, on_chat_room_conference_joined);
linphone_chat_room_cbs_set_conference_left(_chatRoomCbs, on_chat_room_conference_left);
@ -337,33 +337,17 @@ static UICompositeViewDescription *compositeDescription = nil;
//file shared from photo lib
NSString *fileName = dict[@"url"];
[_messageField setText:dict[@"message"]];
NSString *key = [[fileName componentsSeparatedByString:@"."] firstObject];
NSMutableDictionary <NSString *, PHAsset *> * assetDict = [LinphoneUtils photoAssetsDictionary];
PHAsset *phasset = [assetDict objectForKey:key];
if (!phasset) {
// for the images or videos not really in the photo album
[self confirmShare:[self nsDataRead] url:nil fileName:fileName assetId:nil];
} else if ([fileName hasSuffix:@"JPG"] || [fileName hasSuffix:@"PNG"] || [fileName hasSuffix:@"jpg"] || [fileName hasSuffix:@"png"]) {
UIImage *image = [[UIImage alloc] initWithData:[self nsDataRead]];
[self chooseImageQuality:image assetId:[phasset localIdentifier]];
} else if ([fileName hasSuffix:@"MOV"] || [fileName hasSuffix:@"mov"]) {
[self confirmShare:[self nsDataRead] url:nil fileName:nil assetId:[phasset localIdentifier]];
} else {
LOGE(@"Unable to parse file %@",fileName);
}
[self confirmShare:[self nsDataRead] url:nil fileName:fileName];
[defaults removeObjectForKey:@"photoData"];
} else if (dictFile) {
NSString *fileName = dictFile[@"url"];
[_messageField setText:dictFile[@"message"]];
[self confirmShare:[self nsDataRead] url:nil fileName:fileName assetId:nil];
[self confirmShare:[self nsDataRead] url:nil fileName:fileName];
[defaults removeObjectForKey:@"icloudData"];
} else if (dictUrl) {
NSString *url = dictUrl[@"url"];
[_messageField setText:dictUrl[@"message"]];
[self confirmShare:nil url:url fileName:nil assetId:nil];
[self confirmShare:nil url:url fileName:nil];
[defaults removeObjectForKey:@"url"];
}
}
@ -378,6 +362,10 @@ static UICompositeViewDescription *compositeDescription = nil;
}
[self configureForRoom:self.editing];
if (_chatRoom && _markAsRead) {
if (IPAD) {
[VIEW(ChatsListView).tableController loadData];
}
[ChatConversationView markAsRead:_chatRoom];
}
_markAsRead = TRUE;
@ -413,7 +401,7 @@ static UICompositeViewDescription *compositeDescription = nil;
}
}
- (BOOL)sendMessage:(NSString *)message withExterlBodyUrl:(NSURL *)externalUrl withInternalURL:(NSURL *)internalUrl {
- (BOOL)sendMessage:(NSString *)message withExterlBodyUrl:(NSURL *)externalUrl {
if (_chatRoom == NULL) {
LOGW(@"Cannot send message: No chatroom");
return FALSE;
@ -424,11 +412,6 @@ static UICompositeViewDescription *compositeDescription = nil;
linphone_chat_message_set_external_body_url(msg, [[externalUrl absoluteString] UTF8String]);
}
if (internalUrl) {
// 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
linphone_chat_message_send(msg);
@ -470,7 +453,7 @@ static UICompositeViewDescription *compositeDescription = nil;
});
}
- (void)confirmShare:(NSData *)data url:(NSString *)url fileName:(NSString *)fileName assetId:(NSString *)phAssetId {
- (void)confirmShare:(NSData *)data url:(NSString *)url fileName:(NSString *)fileName {
DTActionSheet *sheet = [[DTActionSheet alloc] initWithTitle:@""];
dispatch_async(dispatch_get_main_queue(), ^{
[sheet addButtonWithTitle:NSLocalizedString(@"Send to this friend", nil)
@ -479,11 +462,10 @@ static UICompositeViewDescription *compositeDescription = nil;
[self sendMessageInMessageField];
}
if (url)
[self sendMessage:url withExterlBodyUrl:nil withInternalURL:nil];
else if (fileName)
[self startFileUpload:data withName:fileName];
[self sendMessage:url withExterlBodyUrl:nil];
else
[self startFileUpload:data assetId:phAssetId];}];
[self startFileUpload:data withName:fileName];
}];
[sheet addCancelButtonWithTitle:NSLocalizedString(@"Cancel", nil) block:nil];
[sheet showInView:PhoneMainView.instance.view];
@ -564,7 +546,7 @@ static UICompositeViewDescription *compositeDescription = nil;
}
- (void)sendMessageInMessageField {
if ([self sendMessage:[_messageField text] withExterlBodyUrl:nil withInternalURL:nil]) {
if ([self sendMessage:[_messageField text] withExterlBodyUrl:nil]) {
scrollOnGrowingEnabled = FALSE;
[_messageField setText:@""];
scrollOnGrowingEnabled = TRUE;
@ -647,15 +629,15 @@ static UICompositeViewDescription *compositeDescription = nil;
if ([_imagesArray count] > 0) {
int i = 0;
for (i = 0; i < [_imagesArray count] - 1; ++i) {
[self startImageUpload:[_imagesArray objectAtIndex:i] assetId:[_assetIdsArray objectAtIndex:i] withQuality:[_qualitySettingsArray objectAtIndex:i].floatValue];
[self startImageUpload:[_imagesArray objectAtIndex:i] withQuality:[_qualitySettingsArray objectAtIndex:i].floatValue andMessage:NULL];
}
if (isOneToOne) {
[self startImageUpload:[_imagesArray objectAtIndex:i] assetId:[_assetIdsArray objectAtIndex:i] withQuality:[_qualitySettingsArray objectAtIndex:i].floatValue];
[self startImageUpload:[_imagesArray objectAtIndex:i] withQuality:[_qualitySettingsArray objectAtIndex:i].floatValue andMessage:NULL];
if (![[self.messageField text] isEqualToString:@""]) {
[self sendMessage:[_messageField text] withExterlBodyUrl:nil withInternalURL:nil];
[self sendMessage:[_messageField text] withExterlBodyUrl:nil];
}
} else {
[self startImageUpload:[_imagesArray objectAtIndex:i] assetId:[_assetIdsArray objectAtIndex:i] withQuality:[_qualitySettingsArray objectAtIndex:i].floatValue andMessage:[self.messageField text]];
[self startImageUpload:[_imagesArray objectAtIndex:i] withQuality:[_qualitySettingsArray objectAtIndex:i].floatValue andMessage:[self.messageField text]];
}
[self clearMessageView];
@ -757,28 +739,15 @@ static UICompositeViewDescription *compositeDescription = nil;
#pragma mark ChatRoomDelegate
- (BOOL)startImageUpload:(UIImage *)image assetId:(NSString *)phAssetId withQuality:(float)quality {
- (BOOL)startImageUpload:(UIImage *)image withQuality:(float)quality andMessage:(NSString *)message {
FileTransferDelegate *fileTransfer = [[FileTransferDelegate alloc] init];
[fileTransfer upload:image withassetId:phAssetId forChatRoom:_chatRoom withQuality:quality];
if (message)
[fileTransfer setText:message];
[fileTransfer uploadImage:image forChatRoom:_chatRoom withQuality:quality];
[_tableController scrollToBottom:true];
return TRUE;
}
- (BOOL)startImageUpload:(UIImage *)image assetId:(NSString *)phAssetId withQuality:(float)quality andMessage:(NSString *)message {
FileTransferDelegate *fileTransfer = [[FileTransferDelegate alloc] init];
[fileTransfer setText:message];
[fileTransfer upload:image withassetId:phAssetId forChatRoom:_chatRoom withQuality:quality];
[_tableController scrollToBottom:true];
return TRUE;
}
- (BOOL)startFileUpload:(NSData *)data assetId:(NSString *)phAssetId {
FileTransferDelegate *fileTransfer = [[FileTransferDelegate alloc] init];
[fileTransfer uploadVideo:data withassetId:phAssetId forChatRoom:_chatRoom];
[_tableController scrollToBottom:true];
return TRUE;
}
- (BOOL)startFileUpload:(NSData *)data withName:(NSString *)name {
FileTransferDelegate *fileTransfer = [[FileTransferDelegate alloc] init];
[fileTransfer uploadFile:data forChatRoom:_chatRoom withName:name];
@ -786,8 +755,17 @@ static UICompositeViewDescription *compositeDescription = nil;
return TRUE;
}
- (BOOL)resendFile: (NSData *)data withName:(NSString *)name type:(NSString *)type key:(NSString *)key message:(NSString *)message {
FileTransferDelegate *fileTransfer = [[FileTransferDelegate alloc] init];
if (message)
[fileTransfer setText:message];
[fileTransfer uploadData:data forChatRoom:_chatRoom type:type subtype:type name:name key:key];
[_tableController scrollToBottom:true];
return TRUE;
}
- (void)resendChat:(NSString *)message withExternalUrl:(NSString *)url {
[self sendMessage:message withExterlBodyUrl:[NSURL URLWithString:url] withInternalURL:nil];
[self sendMessage:message withExterlBodyUrl:[NSURL URLWithString:url]];
}
#pragma mark ImagePickerDelegate
@ -836,6 +814,88 @@ static UICompositeViewDescription *compositeDescription = nil;
}
}
+ (void)writeMediaToGallery:(NSString *)name fileType:(NSString *)fileType {
NSString *filePath = [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:name];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:filePath]) {
NSData* data = [NSData dataWithContentsOfFile:filePath];
// define a block , not called immediately. To avoid crash when saving photo before PHAuthorizationStatusNotDetermined.
void (^block)(void)= ^ {
if ([fileType isEqualToString:@"image"] ) {
// we're finished, save the image and update the message
UIImage *image = [UIImage imageWithData:data];
if (!image) {
ChatConversationView *view = VIEW(ChatConversationView);
[view showFileDownloadError];
return;
}
__block PHObjectPlaceholder *placeHolder;
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
PHAssetCreationRequest *request = [PHAssetCreationRequest creationRequestForAssetFromImage:image];
placeHolder = [request placeholderForCreatedAsset];
} completionHandler:^(BOOL success, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
LOGE(@"Cannot save image data downloaded [%@]", [error localizedDescription]);
UIAlertController *errView = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Transfer error", nil)
message:NSLocalizedString(@"Cannot write image to photo library",nil)
preferredStyle:UIAlertControllerStyleAlert];
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 [%@]", [placeHolder localIdentifier]);
}
});
}];
} else if([fileType isEqualToString:@"video"]) {
__block PHObjectPlaceholder *placeHolder;
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
PHAssetCreationRequest *request = [PHAssetCreationRequest creationRequestForAssetFromVideoAtFileURL:[NSURL fileURLWithPath:filePath]];
placeHolder = [request placeholderForCreatedAsset];
} completionHandler:^(BOOL success, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
LOGE(@"Cannot save video data downloaded [%@]", [error localizedDescription]);
UIAlertController *errView = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Transfer error", nil)
message:NSLocalizedString(@"Cannot write video to photo library", nil)
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {}];
[errView addAction:defaultAction];
[PhoneMainView.instance presentViewController:errView animated:YES completion:nil];
} else {
LOGI(@"video saved to [%@]", [placeHolder localIdentifier]);
}
});
}];
}
};
// When you save an image or video to a photo library, make sure that it is allowed. Otherwise, there will be a backup error.
if ([PHPhotoLibrary authorizationStatus] == PHAuthorizationStatusAuthorized) {
block();
} else {
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
dispatch_async(dispatch_get_main_queue(), ^{
if ([PHPhotoLibrary authorizationStatus] == PHAuthorizationStatusAuthorized) {
block();
} else {
[[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Photo's permission", nil) message:NSLocalizedString(@"Photo not authorized", nil) delegate:nil cancelButtonTitle:nil otherButtonTitles:@"Continue", nil] show];
}
});
}];
}
}
}
-(void) writeVideoToGallery:(NSURL *)url {
NSString *localIdentifier;
@ -1110,7 +1170,7 @@ void on_chat_room_chat_message_received(LinphoneChatRoom *cr, const LinphoneEven
[view.tableController scrollToLastUnread:TRUE];
}
void on_chat_room_chat_message_sent(LinphoneChatRoom *cr, const LinphoneEventLog *event_log) {
void on_chat_room_chat_message_sending(LinphoneChatRoom *cr, const LinphoneEventLog *event_log) {
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];
@ -1175,6 +1235,23 @@ void on_chat_room_conference_alert(LinphoneChatRoom *cr, const LinphoneEventLog
[PhoneMainView.instance fullScreen:NO];
}
+ (NSData *)getCacheFileData: (NSString *)name {
NSString *filePath = [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:name];
return [NSData dataWithContentsOfFile:filePath];
}
+ (NSURL *)getCacheFileUrl: (NSString *)name {
NSString *filePath = [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:name];
return [NSURL fileURLWithPath:filePath];
}
+ (void)writeFileInCache:(NSData *)data name:(NSString *)name {
NSString *filePath = [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:name];
[[NSFileManager defaultManager] createFileAtPath:filePath
contents:data
attributes:nil];
}
- (NSURL *)getICloudFileUrl:(NSString *)name {
if (@available(iOS 11.0, *)) {
return [NSURL fileURLWithPath:[LinphoneManager documentFile:name]];
@ -1320,118 +1397,30 @@ void on_chat_room_conference_alert(LinphoneChatRoom *cr, const LinphoneEventLog
[PhoneMainView.instance presentViewController:errView animated:YES completion:nil];
}
+ (NSString *)getKeyFromFileType:(NSString *)fileType fileName:(NSString *)name{
if ([fileType isEqualToString:@"video"]) {
return @"localvideo";
} else if ([fileType isEqualToString:@"image"] || [name hasSuffix:@"JPG"] || [name hasSuffix:@"PNG"] || [name hasSuffix:@"jpg"] || [name hasSuffix:@"png"]) {
return @"localimage";
}
return @"localfile";
}
/* There are three cases: auto download in foreground, auto download in background, on click download*/
+ (void)autoDownload:(LinphoneChatMessage *)message {
ChatConversationView *view = VIEW(ChatConversationView);
//TODO: migrate with "linphone_iphone_file_transfer_recv"
LinphoneContent *content = linphone_chat_message_get_file_transfer_information(message);
NSString *name = [NSString stringWithUTF8String:linphone_content_get_name(content)];
// get download path
NSString *filePath = [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:name];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:filePath]) {
NSData* data = [NSData dataWithContentsOfFile:filePath];
NSString *fileType = [NSString stringWithUTF8String:linphone_content_get_type(content)];
LinphoneContent *content = linphone_chat_message_get_file_transfer_information(message);
NSString *name = [NSString stringWithUTF8String:linphone_content_get_name(content)];
NSString *fileType = [NSString stringWithUTF8String:linphone_content_get_type(content)];
NSString *key = [ChatConversationView getKeyFromFileType:fileType fileName:name];
// define a block , not called immediately. To avoid crash when saving photo before PHAuthorizationStatusNotDetermined.
void (^block)(void)= ^ {
if ([fileType isEqualToString:@"image"]) {
// we're finished, save the image and update the message
UIImage *image = [UIImage imageWithData:data];
if (!image) {
[view showFileDownloadError];
return;
}
__block NSString *createdAssetId = nil;
[[PHPhotoLibrary sharedPhotoLibrary] performChangesAndWait:^{
createdAssetId = [PHAssetCreationRequest creationRequestForAssetFromImage:image].placeholderForCreatedAsset.localIdentifier;
} error:nil];
if (createdAssetId != nil) {
LOGI(@"Image saved to [%@]", createdAssetId);
[LinphoneManager setValueInMessageAppData:createdAssetId
forKey:@"localimage"
inMessage:message];
dispatch_async(dispatch_get_main_queue(), ^{
[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneMessageReceived object:view];
});
} else {
LOGE(@"Cannot save image data downloaded");
[LinphoneManager setValueInMessageAppData:nil forKey:@"localimage" inMessage:message];
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertController *errView = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Transfer error", nil)
message:NSLocalizedString(@"Cannot write image to photo library",
nil)
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {}];
[errView addAction:defaultAction];
[PhoneMainView.instance presentViewController:errView animated:YES completion:nil];
});
}
} else if ([fileType isEqualToString:@"video"]) {
// 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
__block NSString *createdAssetId = nil;
[[PHPhotoLibrary sharedPhotoLibrary] performChangesAndWait:^{
createdAssetId = [PHAssetCreationRequest creationRequestForAssetFromVideoAtFileURL:[NSURL fileURLWithPath:filePath]].placeholderForCreatedAsset.localIdentifier;
} error:nil];
if (createdAssetId != nil) {
LOGI(@"video saved to [%@]", createdAssetId);
[LinphoneManager setValueInMessageAppData:createdAssetId
forKey:@"localvideo"
inMessage:message];
dispatch_async(dispatch_get_main_queue(), ^{
[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneMessageReceived object:view];
});
} else {
LOGE(@"Cannot save video data downloaded");
[LinphoneManager setValueInMessageAppData:nil forKey:@"localvideo" inMessage:message];
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertController *errView = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Transfer error", nil)
message:NSLocalizedString(@"Cannot write video to photo library",
nil)
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {}];
[errView addAction:defaultAction];
[PhoneMainView.instance presentViewController:errView animated:YES completion:nil];
});
}
}
};
// When you save an image or video to a photo library, make sure that it is allowed. Otherwise, there will be a backup error.
if ([fileType isEqualToString:@"image"] || [fileType isEqualToString:@"video"]) {
if ([PHPhotoLibrary authorizationStatus] == PHAuthorizationStatusAuthorized) {
block();
} else {
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
dispatch_async(dispatch_get_main_queue(), ^{
if ([PHPhotoLibrary authorizationStatus] == PHAuthorizationStatusAuthorized) {
block();
} else {
[[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Photo's permission", nil) message:NSLocalizedString(@"Photo not authorized", nil) delegate:nil cancelButtonTitle:nil otherButtonTitles:@"Continue", nil] show];
}
});
}];
}
} else {
NSString *key = @"localfile";
//write file to path
if([view writeFileInICloud:data fileURL:[view getICloudFileUrl:name]]) {
[LinphoneManager setValueInMessageAppData:name forKey:key inMessage:message];
dispatch_async(dispatch_get_main_queue(), ^{
[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneMessageReceived object:view];});
} else {
LOGE(@"[Auto download error] can not save the file %@", name);
}
[LinphoneManager setValueInMessageAppData:name forKey:key inMessage:message];
dispatch_async(dispatch_get_main_queue(), ^{
[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneMessageReceived object:view];
if ([ConfigManager.instance lpConfigBoolForKeyWithKey:@"auto_write_to_gallery_preference"]) {
[ChatConversationView writeMediaToGallery:name fileType:fileType];
}
}
});
}
-(void) documentMenu:(UIDocumentMenuViewController *)documentMenu didPickDocumentPicker:(UIDocumentPickerViewController *)documentPicker {

View file

@ -66,6 +66,12 @@
if (PhoneMainView.instance.currentView == ChatConversationView.compositeViewDescription) {
ChatConversationView *view = VIEW(ChatConversationView);
[view removeCallBacks];
} else if (PhoneMainView.instance.currentView == ChatConversationInfoView.compositeViewDescription) {
ChatConversationInfoView *view = VIEW(ChatConversationInfoView);
[view removeCallbacks];
} else if (PhoneMainView.instance.currentView == RecordingsListView.compositeViewDescription || PhoneMainView.instance.currentView == DevicesListView.compositeViewDescription) {
// To avoid crash
[PhoneMainView.instance changeCurrentView:DialerView.compositeViewDescription];
}
[CoreManager.instance stopLinphoneCore];
}
@ -649,21 +655,15 @@
LOGI(@"User declined video proposal");
if (call != linphone_core_get_current_call(LC))
return;
LinphoneCallParams *params = linphone_core_create_call_params(LC, call);
linphone_call_accept_update(call, params);
linphone_call_params_destroy(params);
[CallManager.instance acceptVideoWithCall:call confirm:FALSE];
} else if ([response.actionIdentifier isEqual:@"Accept"]) {
LOGI(@"User accept video proposal");
if (call != linphone_core_get_current_call(LC))
return;
[[UNUserNotificationCenter currentNotificationCenter] removeAllDeliveredNotifications];
[PhoneMainView.instance changeCurrentView:CallView.compositeViewDescription];
LinphoneCallParams *params = linphone_core_create_call_params(LC, call);
linphone_call_params_enable_video(params, TRUE);
linphone_call_accept_update(call, params);
linphone_call_params_destroy(params);
[PhoneMainView.instance changeCurrentView:CallView.compositeViewDescription];
[CallManager.instance acceptVideoWithCall:call confirm:TRUE];
} else if ([response.actionIdentifier isEqual:@"Confirm"]) {
if (linphone_core_get_current_call(LC) == call)
linphone_call_set_authentication_token_verified(call, YES);
@ -694,6 +694,7 @@
[PhoneMainView.instance changeCurrentView:ChatsListView.compositeViewDescription];
}
} else if ([response.notification.request.content.categoryIdentifier isEqual:@"video_request"]) {
if (!call) return;
[PhoneMainView.instance changeCurrentView:CallView.compositeViewDescription];
NSTimer *videoDismissTimer = nil;
UIConfirmationDialog *sheet = [UIConfirmationDialog ShowWithMessage:response.notification.request.content.body
@ -703,21 +704,14 @@
LOGI(@"User declined video proposal");
if (call != linphone_core_get_current_call(LC))
return;
LinphoneCallParams *params = linphone_core_create_call_params(LC, call);
linphone_call_accept_update(call, params);
linphone_call_params_destroy(params);
[CallManager.instance acceptVideoWithCall:call confirm:FALSE];
[videoDismissTimer invalidate];
}
onConfirmationClick:^() {
LOGI(@"User accept video proposal");
if (call != linphone_core_get_current_call(LC))
return;
LinphoneCallParams *params = linphone_core_create_call_params(LC, call);
linphone_call_params_enable_video(params, TRUE);
linphone_call_accept_update(call, params);
linphone_call_params_destroy(params);
[CallManager.instance acceptVideoWithCall:call confirm:TRUE];
[videoDismissTimer invalidate];
}
inController:PhoneMainView.instance];

View file

@ -338,7 +338,8 @@
[self setCString:linphone_core_get_file_transfer_server(LC) forKey:@"file_transfer_server_url_preference"];
int maxSize = linphone_core_get_max_size_for_auto_download_incoming_files(LC);
[self setObject:maxSize==0 ? @"Always" : (maxSize==-1 ? @"Nerver" : @"Customize") forKey:@"auto_download_mode"];
[self setInteger:maxSize forKey:@"auto_download_incoming_files_max_size"];
[self setInteger:maxSize forKey:@"auto_download_incoming_files_max_size"];
[self setBool:[lm lpConfigBoolForKey:@"auto_write_to_gallery_preference" withDefault:YES] forKey:@"auto_write_to_gallery_mode"];
}
// network section
@ -547,11 +548,10 @@
linphone_address_set_domain(linphoneAddress, [domain UTF8String]);
linphone_address_set_display_name(linphoneAddress, (displayName.length ? displayName.UTF8String : NULL));
const char *identity = linphone_address_as_string(linphoneAddress);
linphone_address_destroy(linphoneAddress);
const char *password = [accountPassword UTF8String];
const char *ha1 = [accountHa1 UTF8String];
if (linphone_proxy_config_set_identity(proxyCfg, identity) == -1) {
if (linphone_proxy_config_set_identity_address(proxyCfg, linphoneAddress) == -1) {
error = NSLocalizedString(@"Invalid username or domain", nil);
goto bad_proxy;
}
@ -586,7 +586,7 @@
[LinphoneManager.instance configurePushTokenForProxyConfig:proxyCfg];
linphone_proxy_config_enable_register(proxyCfg, is_enabled);
linphone_proxy_config_enable_avpf(proxyCfg, use_avpf);
linphone_proxy_config_set_avpf_mode(proxyCfg, use_avpf);
linphone_proxy_config_set_expires(proxyCfg, expire);
if (is_default) {
linphone_core_set_default_proxy_config(LC, proxyCfg);
@ -636,7 +636,9 @@
bad_proxy:
if (proxy)
ms_free(proxy);
if (linphoneAddress)
linphone_address_destroy(linphoneAddress);
// in case of error, show an alert to the user
if (error != nil) {
linphone_proxy_config_done(proxyCfg);
@ -778,6 +780,7 @@
}
linphone_core_set_max_size_for_auto_download_incoming_files(LC, maxSize);
[lm lpConfigSetString:[self stringForKey:@"auto_download_mode"] forKey:@"auto_download_mode"];
[lm lpConfigSetBool:[self boolForKey:@"auto_write_to_gallery_mode"] forKey:@"auto_write_to_gallery_preference"];
// network section
BOOL edgeOpt = [self boolForKey:@"edge_opt_preference"];

View file

@ -388,7 +388,7 @@ static int check_should_migrate_images(void *data, int argc, char **argv, char *
withDefault:@"sip.linphone.org"]
.UTF8String) != 0) {
LOGI(@"Migrating proxy config to use AVPF");
linphone_proxy_config_enable_avpf(proxy, TRUE);
linphone_proxy_config_set_avpf_mode(proxy, LinphoneAVPFEnabled);
}
proxies = proxies->next;
}
@ -511,7 +511,7 @@ static void migrateWizardToAssistant(const char *entry, void *user_data) {
+ (void)dumpLcConfig {
if (theLinphoneCore) {
LpConfig *conf = LinphoneManager.instance.configDb;
char *config = lp_config_dump(conf);
char *config = linphone_config_dump(conf);
LOGI(@"\n%s", config);
ms_free(config);
}
@ -720,8 +720,8 @@ static void linphone_iphone_popup_password_request(LinphoneCore *lc, LinphoneAut
// let the wizard handle its own errors
if ([PhoneMainView.instance currentView] != AssistantView.compositeViewDescription) {
const char * realmC = linphone_auth_info_get_realm(auth_info);
const char * usernameC = linphone_auth_info_get_username(auth_info);
const char * domainC = linphone_auth_info_get_domain(auth_info);
const char * usernameC = linphone_auth_info_get_username(auth_info) ? : "";
const char * domainC = linphone_auth_info_get_domain(auth_info) ? : "";
static UIAlertController *alertView = nil;
// avoid having multiple popups
@ -1365,6 +1365,8 @@ void popup_link_account_cb(LinphoneAccountCreator *creator, LinphoneAccountCreat
if (theLinphoneCore != nil) { // just in case application terminate before linphone core initialization
// rare case, remove duplicated fileTransferDelegates to avoid crash
[_fileTransferDelegates setArray:[[NSSet setWithArray:_fileTransferDelegates] allObjects]];
for (FileTransferDelegate *ftd in _fileTransferDelegates) {
[ftd stopAndDestroy];
}
@ -1682,7 +1684,7 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) {
factory = factoryIpad;
}
_configDb = linphone_config_new_for_shared_core(kLinphoneMsgNotificationAppGroupId.UTF8String, @"linphonerc".UTF8String, factory.UTF8String);
lp_config_clean_entry(_configDb, "misc", "max_calls");
linphone_config_clean_entry(_configDb, "misc", "max_calls");
}
#pragma mark - Audio route Functions
@ -2015,7 +2017,7 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) {
- (void)configureVbrCodecs {
PayloadType *pt;
int bitrate = lp_config_get_int(
int bitrate = linphone_config_get_int(
_configDb, "audio", "codec_bitrate_limit",
kLinphoneAudioVbrCodecDefaultBitrate); /*default value is in linphonerc or linphonerc-factory*/
const MSList *audio_codecs = linphone_core_get_audio_codecs(theLinphoneCore);
@ -2070,7 +2072,7 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) {
- (void)lpConfigSetString:(NSString *)value forKey:(NSString *)key inSection:(NSString *)section {
if (!key)
return;
lp_config_set_string(_configDb, [section UTF8String], [key UTF8String], value ? [value UTF8String] : NULL);
linphone_config_set_string(_configDb, [section UTF8String], [key UTF8String], value ? [value UTF8String] : NULL);
}
- (NSString *)lpConfigStringForKey:(NSString *)key {
return [self lpConfigStringForKey:key withDefault:nil];
@ -2084,7 +2086,7 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) {
- (NSString *)lpConfigStringForKey:(NSString *)key inSection:(NSString *)section withDefault:(NSString *)defaultValue {
if (!key)
return defaultValue;
const char *value = lp_config_get_string(_configDb, [section UTF8String], [key UTF8String], NULL);
const char *value = linphone_config_get_string(_configDb, [section UTF8String], [key UTF8String], NULL);
return value ? [NSString stringWithUTF8String:value] : defaultValue;
}
@ -2094,7 +2096,7 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) {
- (void)lpConfigSetInt:(int)value forKey:(NSString *)key inSection:(NSString *)section {
if (!key)
return;
lp_config_set_int(_configDb, [section UTF8String], [key UTF8String], (int)value);
linphone_config_set_int(_configDb, [section UTF8String], [key UTF8String], (int)value);
}
- (int)lpConfigIntForKey:(NSString *)key {
return [self lpConfigIntForKey:key withDefault:-1];
@ -2108,7 +2110,7 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) {
- (int)lpConfigIntForKey:(NSString *)key inSection:(NSString *)section withDefault:(int)defaultValue {
if (!key)
return defaultValue;
return lp_config_get_int(_configDb, [section UTF8String], [key UTF8String], (int)defaultValue);
return linphone_config_get_int(_configDb, [section UTF8String], [key UTF8String], (int)defaultValue);
}
- (void)lpConfigSetBool:(BOOL)value forKey:(NSString *)key {

View file

@ -1,11 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@ -30,6 +28,7 @@
<outlet property="messageImageView" destination="yMW-cT-bpU" id="MNr-F2-abQ"/>
<outlet property="messageText" destination="cx9-0K-P9L" id="kPh-s4-Ioy"/>
<outlet property="playButton" destination="cvc-tl-Pcf" id="eKJ-2T-LUl"/>
<outlet property="plusLongGestureRecognizer" destination="daf-cW-dRj" id="O5u-t0-uMe"/>
<outlet property="resendRecognizer" destination="5ZI-Ip-lGl" id="G2r-On-6mV"/>
<outlet property="totalView" destination="8I3-n2-0kS" id="aa8-j9-saW"/>
</connections>
@ -85,6 +84,29 @@
</imageView>
</subviews>
</view>
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="UzU-cc-LbF" userLabel="fileView">
<rect key="frame" x="13" y="10" width="230" height="50"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Label" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="WkE-rP-Y0R" userLabel="fileName">
<rect key="frame" x="0.0" y="0.0" width="180" 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>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="IGl-nl-xIE" userLabel="fileButton">
<rect key="frame" x="180" y="0.0" width="50" height="50"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" red="1" green="0.39905477280000001" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<state key="normal" title="Open"/>
<connections>
<action selector="onFileClick:" destination="-1" eventType="touchUpInside" id="JRN-AA-UEJ"/>
</connections>
</button>
</subviews>
</view>
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="GmN-7v-uuO" userLabel="imageSubView">
<rect key="frame" x="31" y="50" width="299" height="87"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES" flexibleMaxY="YES"/>
@ -93,7 +115,7 @@
<rect key="frame" x="10" y="51" width="277" height="4"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
</progressView>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="N75-gL-R6t" userLabel="downloadButton" customClass="UIRoundBorderedButton">
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="N75-gL-R6t" userLabel="downloadButton" customClass="UIRoundBorderedButton">
<rect key="frame" x="87" y="58" width="113" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
<accessibility key="accessibilityConfiguration" label="Download"/>
@ -105,7 +127,7 @@
<action selector="onDownloadClick:" destination="-1" eventType="touchUpInside" id="8BO-9E-iOX"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6dl-Nz-rdv" userLabel="cancelButton" customClass="UIRoundBorderedButton">
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6dl-Nz-rdv" userLabel="cancelButton" customClass="UIRoundBorderedButton">
<rect key="frame" x="87" y="58" width="113" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
<accessibility key="accessibilityConfiguration" label="Cancel"/>
@ -120,8 +142,8 @@
</button>
</subviews>
</view>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="cvc-tl-Pcf" userLabel="playButton" customClass="UIRoundBorderedButton">
<rect key="frame" x="158" y="106" width="50" height="25"/>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="cvc-tl-Pcf" userLabel="playButton" customClass="UIRoundBorderedButton">
<rect key="frame" x="157" y="106" width="50" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" label="Cancel"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
@ -145,33 +167,11 @@
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
<accessibility key="accessibilityConfiguration" label="Delivery failed"/>
</imageView>
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="UzU-cc-LbF" userLabel="fileView">
<rect key="frame" x="13" y="10" width="230" height="50"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Label" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="WkE-rP-Y0R" userLabel="fileName">
<rect key="frame" x="0.0" y="0.0" width="180" 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>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="IGl-nl-xIE" userLabel="fileButton">
<rect key="frame" x="180" y="0.0" width="50" height="50"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" red="1" green="0.39905477280000001" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<state key="normal" title="Open"/>
<connections>
<action selector="onFileClick:" destination="-1" eventType="touchUpInside" id="JRN-AA-UEJ"/>
</connections>
</button>
</subviews>
</view>
</subviews>
<connections>
<outletCollection property="gestureRecognizers" destination="5ZI-Ip-lGl" appends="YES" id="1iY-46-rRR"/>
<outletCollection property="gestureRecognizers" destination="aDF-hC-ddO" appends="YES" id="FIv-pl-I8J"/>
<outletCollection property="gestureRecognizers" destination="daf-cW-dRj" appends="YES" id="qgk-YT-Grl"/>
</connections>
</view>
</subviews>
@ -189,14 +189,19 @@
<action selector="onImageClick:" destination="-1" id="feN-LT-89b"/>
</connections>
</tapGestureRecognizer>
<pongPressGestureRecognizer allowableMovement="10" minimumPressDuration="0.5" id="daf-cW-dRj" userLabel="plusClick">
<connections>
<action selector="onPlusClick:" destination="-1" id="oFW-FN-6xV"/>
</connections>
</pongPressGestureRecognizer>
</objects>
<resources>
<image name="avatar.png" width="259" height="259"/>
<image name="chat_read.png" width="25" height="25"/>
<image name="avatar.png" width="414.39999389648438" height="414.39999389648438"/>
<image name="chat_read.png" width="20" height="20"/>
<image name="color_A.png" width="2" height="2"/>
<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="26"/>
<image name="linphone_logo.png" width="41.599998474121094" height="42.400001525878906"/>
</resources>
</document>

View file

@ -72,7 +72,7 @@
// Update to default state
LinphoneProxyConfig *config = linphone_core_get_default_proxy_config(LC);
messagesUnreadCount = lp_config_get_int(linphone_core_get_config(LC), "app", "voice_mail_messages_count", 0);
messagesUnreadCount = linphone_config_get_int(linphone_core_get_config(LC), "app", "voice_mail_messages_count", 0);
[self proxyConfigUpdate:config];
[self updateUI:linphone_core_get_calls_nb(LC)];
@ -149,7 +149,7 @@
LOGI(@"Received new NOTIFY from voice mail: there is/are now %d message(s) unread", messagesUnreadCount);
// save in lpconfig for future
lp_config_set_int(linphone_core_get_config(LC), "app", "voice_mail_messages_count", messagesUnreadCount);
linphone_config_set_int(linphone_core_get_config(LC), "app", "voice_mail_messages_count", messagesUnreadCount);
[self updateVoicemail];
}

View file

@ -40,6 +40,7 @@
@property(strong, nonatomic) IBOutlet UITapGestureRecognizer *imageGestureRecognizer;
@property (weak, nonatomic) IBOutlet UIButton *fileButton;
@property (weak, nonatomic) IBOutlet UIView *fileView;
@property (strong, nonatomic) IBOutlet UILongPressGestureRecognizer *plusLongGestureRecognizer;
- (void)setEvent:(LinphoneEventLog *)event;
- (void)setChatMessage:(LinphoneChatMessage *)message;
@ -50,6 +51,7 @@
- (IBAction)onResendClick:(id)event;
- (IBAction)onPlayClick:(id)sender;
- (IBAction)onFileClick:(id)sender;
- (IBAction)onPlusClick:(id)sender;
@end

View file

@ -52,6 +52,7 @@
chatTableView = VIEW(ChatConversationView).tableController;
videoDefaultSize = CGSizeMake(320, 240);
assetIsLoaded = FALSE;
self.contentView.userInteractionEnabled = NO;
}
return self;
}
@ -71,31 +72,26 @@
- (void)setChatMessage:(LinphoneChatMessage *)amessage {
_imageGestureRecognizer.enabled = NO;
_plusLongGestureRecognizer.enabled = NO;
_messageImageView.image = nil;
_finalImage.image = nil;
_finalImage.hidden = TRUE;
_fileTransferProgress.progress = 0;
assetIsLoaded = FALSE;
[self disconnectFromFileDelegate];
/* As the cell UI will be reset, fileTransDelegate need to be reconnected. Otherwise, the UIProgressView will not work */
[self disconnectFromFileDelegate];
if (amessage) {
const LinphoneContent *c = linphone_chat_message_get_file_transfer_information(amessage);
if (c) {
const char *name = linphone_content_get_name(c);
for (FileTransferDelegate *aftd in [LinphoneManager.instance fileTransferDelegates]) {
if (linphone_chat_message_get_file_transfer_information(aftd.message) &&
(linphone_chat_message_is_outgoing(aftd.message) == linphone_chat_message_is_outgoing(amessage)) &&
strcmp(name, linphone_content_get_name(
linphone_chat_message_get_file_transfer_information(aftd.message))) == 0) {
LOGI(@"Chat message [%p] with file transfer delegate [%p], connecting to it!", amessage, aftd);
[self connectToFileDelegate:aftd];
break;
}
for (FileTransferDelegate *aftd in [LinphoneManager.instance fileTransferDelegates]) {
if (aftd.message == amessage && linphone_chat_message_get_state(amessage) == LinphoneChatMessageStateFileTransferInProgress) {
LOGI(@"Chat message [%p] with file transfer delegate [%p], connecting to it!", amessage, aftd);
[self connectToFileDelegate:aftd];
break;
}
}
}
[super setChatMessage:amessage];
[super setChatMessageForCbs:amessage];
}
- (void) loadImageAsset:(PHAsset*) asset image:(UIImage *)image {
@ -106,6 +102,7 @@
_messageImageView.hidden = YES;
_finalImage.hidden = NO;
_fileView.hidden = YES;
_plusLongGestureRecognizer.enabled = YES;
[self layoutSubviews];
});
}
@ -128,10 +125,13 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100;
}];
}
- (void) loadFileAsset {
- (void) loadFileAsset:(NSString *)name {
NSString *text = [NSString stringWithFormat:@"📎 %@",name];
_fileName.text = text;
dispatch_async(dispatch_get_main_queue(), ^{
_fileName.hidden = _fileView.hidden = _fileButton.hidden = NO;
_imageGestureRecognizer.enabled = NO;
_plusLongGestureRecognizer.enabled = NO;
});
}
@ -143,6 +143,7 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100;
[_messageImageView stopLoading];
_messageImageView.hidden = YES;
_imageGestureRecognizer.enabled = YES;
_plusLongGestureRecognizer.enabled = YES;
_finalImage.hidden = NO;
[self layoutSubviews];
});
@ -153,195 +154,194 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100;
LOGW(@"Cannot update message room cell: NULL message");
return;
}
[super update];
[super update];
const char *url = linphone_chat_message_get_external_body_url(self.message);
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];
NSString *localVideo = [LinphoneManager getMessageAppDataForKey:@"localvideo" inMessage:self.message];
NSString *localFile = [LinphoneManager getMessageAppDataForKey:@"localfile" inMessage:self.message];
assert(is_external || localImage || localVideo || localFile);
if (!(localImage || localVideo || localFile)) {
// If the file has been downloaded in background, save it in the folders and display it.
ChatConversationView *view = VIEW(ChatConversationView);
//TODO: migrate with "linphone_iphone_file_transfer_recv"
LinphoneContent *content = linphone_chat_message_get_file_transfer_information(self.message);
NSString *name = [NSString stringWithUTF8String:linphone_content_get_name(content)];
// get download path
NSString *filePath = [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:name];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:filePath]) {
NSData* data = [NSData dataWithContentsOfFile:filePath];
NSString *fileType = [NSString stringWithUTF8String:linphone_content_get_type(content)];
// define a block , not called immediately. To avoid crash when saving photo before PHAuthorizationStatusNotDetermined.
void (^block)(void)= ^ {
if ([fileType isEqualToString:@"image"]) {
// we're finished, save the image and update the message
UIImage *image = [UIImage imageWithData:data];
if (!image) {
[view showFileDownloadError];
return;
}
__block PHObjectPlaceholder *placeHolder;
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
PHAssetCreationRequest *request = [PHAssetCreationRequest creationRequestForAssetFromImage:image];
placeHolder = [request placeholderForCreatedAsset];
} completionHandler:^(BOOL success, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
LOGE(@"Cannot save image data downloaded [%@]", [error localizedDescription]);
[LinphoneManager setValueInMessageAppData:nil forKey:@"localimage" inMessage:self.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"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {}];
[errView addAction:defaultAction];
[PhoneMainView.instance presentViewController:errView animated:YES completion:nil];
} else {
LOGI(@"Image saved to [%@]", [placeHolder localIdentifier]);
[LinphoneManager setValueInMessageAppData:[placeHolder localIdentifier]
forKey:@"localimage"
inMessage:self.message];
[self updateButtons:[LinphoneManager getMessageAppDataForKey:@"localimage" inMessage:self.message] localVideo:localVideo localFile:localFile];
}
});
}];
} else if([fileType isEqualToString:@"video"]) {
// 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
__block PHObjectPlaceholder *placeHolder;
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
PHAssetCreationRequest *request = [PHAssetCreationRequest creationRequestForAssetFromVideoAtFileURL:[NSURL fileURLWithPath:filePath]];
placeHolder = [request placeholderForCreatedAsset];
} completionHandler:^(BOOL success, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
LOGE(@"Cannot save video data downloaded [%@]", [error localizedDescription]);
[LinphoneManager setValueInMessageAppData:nil forKey:@"localvideo" inMessage:self.message];
UIAlertController *errView = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Transfer error", nil)
message:NSLocalizedString(@"Cannot write video to photo library",
nil)
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {}];
[errView addAction:defaultAction];
[PhoneMainView.instance presentViewController:errView animated:YES completion:nil];
} else {
LOGI(@"video saved to [%@]", [placeHolder localIdentifier]);
[LinphoneManager setValueInMessageAppData:[placeHolder localIdentifier]
forKey:@"localvideo"
inMessage:self.message];
[self updateButtons:localImage localVideo:[LinphoneManager getMessageAppDataForKey:@"localvideo" inMessage:self.message] localFile:localFile];
}
});
}];
}
};
// When you save an image or video to a photo library, make sure that it is allowed. Otherwise, there will be a backup error.
if ([fileType isEqualToString:@"image"] || [fileType isEqualToString:@"video"]) {
if ([PHPhotoLibrary authorizationStatus] == PHAuthorizationStatusAuthorized) {
block();
} else {
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
dispatch_async(dispatch_get_main_queue(), ^{
if ([PHPhotoLibrary authorizationStatus] == PHAuthorizationStatusAuthorized) {
block();
} else {
[[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Photo's permission", nil) message:NSLocalizedString(@"Photo not authorized", nil) delegate:nil cancelButtonTitle:nil otherButtonTitles:@"Continue", nil] show];
}
});
}];
}
LinphoneContent *fileContent = linphone_chat_message_get_file_transfer_information(self.message);
if (fileContent == nil) {
LOGW(@"file content is null");
return;
}
BOOL is_outgoing = linphone_chat_message_is_outgoing(self.message);
if (!is_outgoing) {
LinphoneChatMessageState state = linphone_chat_message_get_state(self.message);
if (state != LinphoneChatMessageStateFileTransferDone && state != LinphoneChatMessageStateDisplayed) {
if (state == LinphoneChatMessageStateFileTransferInProgress) {
_cancelButton.hidden = _fileTransferProgress.hidden = NO;
_downloadButton.hidden = YES;
_playButton.hidden = YES;
_fileName.hidden = _fileView.hidden = _fileButton.hidden =YES;
} else {
NSString *key = @"localfile";
//write file to path
if([view writeFileInICloud:data fileURL:[view getICloudFileUrl:name]]) {
dispatch_async(dispatch_get_main_queue(), ^{
[LinphoneManager setValueInMessageAppData:name forKey:key inMessage:self.message];
[self updateButtons:localImage localVideo:localVideo localFile:[LinphoneManager getMessageAppDataForKey:@"localfile" inMessage:self.message]];
});
} else {
LOGE(@"[Auto download error] can not save the file %@", name);
}
_downloadButton.hidden = NO;
_cancelButton.hidden = _fileTransferProgress.hidden = YES;
_playButton.hidden = YES;
_fileName.hidden = _fileView.hidden = _fileButton.hidden = YES;
}
return;
}
}
NSString *fileType = [NSString stringWithUTF8String:linphone_content_get_type(fileContent)];
NSString *fileName = [NSString stringWithUTF8String:linphone_content_get_name(fileContent)];
NSString *filePath = [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:fileName];
[self updateButtons:localImage localVideo:localVideo localFile:localFile];
}
- (void)updateButtons: (NSString *)localImage localVideo:(NSString *)localVideo localFile:(NSString *)localFile {
LinphoneContent *fileContent = linphone_chat_message_get_file_transfer_information(self.message);
NSString *type = fileContent ? [NSString stringWithUTF8String:linphone_content_get_type(fileContent)] : nil;
if (!(localImage || localVideo || localFile)) {
_playButton.hidden = YES;
_fileName.hidden = _fileView.hidden = _fileButton.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 = _fileView.hidden = _fileButton.hidden = YES;
} else {
if(!assetIsLoaded) {
assetIsLoaded = TRUE;
if (localImage) {
// we did not load the image yet, so start doing so
if (_messageImageView.image == nil) {
[self loadFirstImage:localImage type:PHAssetMediaTypeImage];
_imageGestureRecognizer.enabled = YES;
}
}
else if (localVideo) {
if (_messageImageView.image == nil) {
[self loadFirstImage:localVideo type:PHAssetMediaTypeVideo];
_imageGestureRecognizer.enabled = NO;
}
}
else if (localFile) {
if ([type isEqualToString:@"video"]) {
UIImage* image = [UIChatBubbleTextCell getImageFromVideoUrl:[VIEW(ChatConversationView) getICloudFileUrl:localFile]];
[self loadImageAsset:nil image:image];
_imageGestureRecognizer.enabled = NO;
} else if ([localFile hasSuffix:@"JPG"] || [localFile hasSuffix:@"PNG"] || [localFile hasSuffix:@"jpg"] || [localFile hasSuffix:@"png"]) {
NSData *data = [NSData dataWithContentsOfURL:[VIEW(ChatConversationView) getICloudFileUrl:localFile]];
UIImage *image = [[UIImage alloc] initWithData:data];
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
// already downloaded
if (!assetIsLoaded) {
assetIsLoaded = TRUE;
NSString *key = [ChatConversationView getKeyFromFileType:fileType fileName:fileName];
if ([key isEqualToString:@"localimage"]) {
// we did not load the image yet, so start doing so
if (_messageImageView.image == nil) {
NSData* data = [NSData dataWithContentsOfFile:filePath];
UIImage *image = [[UIImage alloc] initWithData:data];
if (image) {
[self loadImageAsset:nil image:image];
_imageGestureRecognizer.enabled = YES;
} else {
NSString *text = [NSString stringWithFormat:@"📎 %@",localFile];
_fileName.text = text;
[self loadFileAsset];
// compability with other platforms
[self loadFileAsset:fileName];
}
}
} else if ([key isEqualToString:@"localvideo"]) {
if (_messageImageView.image == nil) {
UIImage* image = [UIChatBubbleTextCell getImageFromVideoUrl:[ChatConversationView getCacheFileUrl:fileName]];
if (image) {
[self loadImageAsset:nil image:image];
_imageGestureRecognizer.enabled = NO;
} else {
// compability with other platforms
[self loadFileAsset:fileName];
}
}
} else if ([key isEqualToString:@"localfile"]) {
if ([fileType isEqualToString:@"video"]) {
UIImage* image = [UIChatBubbleTextCell getImageFromVideoUrl:[ChatConversationView getCacheFileUrl:fileName]];
[self loadImageAsset:nil image:image];
_imageGestureRecognizer.enabled = NO;
} else if ([fileName hasSuffix:@"JPG"] || [fileName hasSuffix:@"PNG"] || [fileName hasSuffix:@"jpg"] || [fileName hasSuffix:@"png"]) {
NSData *data = [ChatConversationView getCacheFileData:fileName];
UIImage *image = [[UIImage alloc] initWithData:data];
[self loadImageAsset:nil image:image];
_imageGestureRecognizer.enabled = YES;
} else {
[self loadFileAsset:fileName];
}
}
if (!(localImage || localVideo || localFile)) {
// If the file has been downloaded in background, save it in the folders and display it.
[LinphoneManager setValueInMessageAppData:fileName forKey:key inMessage:self.message];
dispatch_async(dispatch_get_main_queue(), ^ {
if ([ConfigManager.instance lpConfigBoolForKeyWithKey:@"auto_write_to_gallery_preference"]) {
[ChatConversationView writeMediaToGallery:fileName fileType:fileType];
}
});
}
}
[self uploadingImage:fileType localFile:localFile];
} else {
// support previous methode:
if (!(localImage || localVideo || localFile)) {
_playButton.hidden = YES;
_fileName.hidden = _fileView.hidden = _fileButton.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 = _fileView.hidden = _fileButton.hidden = YES;
} else {
if(!assetIsLoaded) {
assetIsLoaded = TRUE;
if (localImage) {
// we did not load the image yet, so start doing so
if (_messageImageView.image == nil) {
[self loadFirstImage:localImage type:PHAssetMediaTypeImage];
_imageGestureRecognizer.enabled = YES;
dispatch_async(dispatch_get_main_queue(), ^ {
UIImage *image = [chatTableView.imagesInChatroom objectForKey:localImage];
NSString *name = [NSString stringWithFormat:@"%li-%f.jpg", (long)image.hash, [NSDate timeIntervalSinceReferenceDate]];
NSData *data = UIImageJPEGRepresentation(image, 1);
[ChatConversationView writeFileInCache:data name:name];
[LinphoneManager setValueInMessageAppData:name forKey:@"localimage" inMessage:self.message];
});
}
} else if (localVideo) {
if (_messageImageView.image == nil) {
[self loadFirstImage:localVideo type:PHAssetMediaTypeVideo];
_imageGestureRecognizer.enabled = NO;
dispatch_async(dispatch_get_main_queue(), ^ {
PHFetchResult<PHAsset *> *assets = [PHAsset fetchAssetsWithLocalIdentifiers:[NSArray arrayWithObject:localVideo] options:nil];
if (![assets firstObject])
return;
PHAsset *asset = [assets firstObject];
if (asset.mediaType != PHAssetMediaTypeVideo)
return;
PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init];
options.version = PHImageRequestOptionsVersionCurrent;
options.deliveryMode = PHVideoRequestOptionsDeliveryModeAutomatic;
[[PHImageManager defaultManager] requestAVAssetForVideo:asset options:options resultHandler:^(AVAsset * _Nullable asset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) {
AVURLAsset *urlAsset = (AVURLAsset *)asset;
NSData *data = [NSData dataWithContentsOfURL:urlAsset.URL];
NSString *name = [NSString stringWithFormat:@"IMG-%f.MOV", [NSDate timeIntervalSinceReferenceDate]];
[ChatConversationView writeFileInCache:data name:name];
[LinphoneManager setValueInMessageAppData:name forKey:@"localvideo" inMessage:self.message];
}];
});
}
} else if (localFile) {
dispatch_async(dispatch_get_main_queue(), ^ {
NSURL *url = [VIEW(ChatConversationView) getICloudFileUrl:localFile];
NSData *data = [NSData dataWithContentsOfURL:url];
[ChatConversationView writeFileInCache:data name:localFile];
});
if ([fileType isEqualToString:@"video"]) {
UIImage* image = [UIChatBubbleTextCell getImageFromVideoUrl:[VIEW(ChatConversationView) getICloudFileUrl:localFile]];
[self loadImageAsset:nil image:image];
_imageGestureRecognizer.enabled = NO;
} else if ([localFile hasSuffix:@"JPG"] || [localFile hasSuffix:@"PNG"] || [localFile hasSuffix:@"jpg"] || [localFile hasSuffix:@"png"]) {
NSData *data = [NSData dataWithContentsOfURL:[VIEW(ChatConversationView) getICloudFileUrl:localFile]];
UIImage *image = [[UIImage alloc] initWithData:data];
[self loadImageAsset:nil image:image];
_imageGestureRecognizer.enabled = YES;
} else {
[self loadFileAsset:fileName];
}
}
}
}
[self uploadingImage:fileType localFile:localFile];
}
// we are uploading the image
if (_ftd.message != nil) {
_cancelButton.hidden = _fileTransferProgress.hidden = super.notDelivered ? YES : NO;
_downloadButton.hidden = YES;
_playButton.hidden = YES;
_fileName.hidden = _fileView.hidden = _fileButton.hidden =YES;
} else {
_cancelButton.hidden = _fileTransferProgress.hidden = _downloadButton.hidden = YES;
_playButton.hidden = ![type isEqualToString:@"video"];
_fileName.hidden = _fileView.hidden = _fileButton.hidden = localFile ? NO : YES;
}
}
}
}
- (void)uploadingImage:(NSString *)fileType localFile:(NSString *)localFile {
// we are uploading the image
if (_ftd.message != nil) {
_cancelButton.hidden = _fileTransferProgress.hidden = super.notDelivered ? YES : NO;
_downloadButton.hidden = YES;
_playButton.hidden = YES;
_fileName.hidden = _fileView.hidden = _fileButton.hidden =YES;
} else {
_cancelButton.hidden = _fileTransferProgress.hidden = _downloadButton.hidden = YES;
_playButton.hidden = ![fileType isEqualToString:@"video"];
_fileName.hidden = _fileView.hidden = _fileButton.hidden = localFile ? NO : YES;
}
}
- (void)loadFirstImage:(NSString *)key type:(PHAssetMediaType)type {
@ -390,11 +390,29 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100;
}
- (IBAction)onPlayClick:(id)sender {
NSString *localVideo = [LinphoneManager getMessageAppDataForKey:@"localvideo" inMessage:self.message];
NSString *localFile = [LinphoneManager getMessageAppDataForKey:@"localfile" inMessage:self.message];
if (localVideo && [[NSFileManager defaultManager] fileExistsAtPath:[[LinphoneManager cacheDirectory] stringByAppendingPathComponent:localVideo]]) {
AVPlayer *player = [AVPlayer playerWithURL:[ChatConversationView getCacheFileUrl:localVideo]];
[self playVideoByPlayer:player];
return;
} else if (localFile && [[NSFileManager defaultManager] fileExistsAtPath:[[LinphoneManager cacheDirectory] stringByAppendingPathComponent:localFile]]) {
AVPlayer *player = [AVPlayer playerWithURL:[ChatConversationView getCacheFileUrl:localFile]];
[self playVideoByPlayer:player];
return;
}
PHAsset *asset = [_messageImageView asset];
if (!asset) {
NSString *localFile = [LinphoneManager getMessageAppDataForKey:@"localfile" inMessage:self.message];
AVPlayer *player = [AVPlayer playerWithURL:[VIEW(ChatConversationView) getICloudFileUrl:localFile]];
NSURL *url = [VIEW(ChatConversationView) getICloudFileUrl:localFile];
AVPlayer *player = [AVPlayer playerWithURL:url];
[self playVideoByPlayer:player];
dispatch_async(dispatch_get_main_queue(), ^ {
NSData *data = [NSData dataWithContentsOfURL:url];
[ChatConversationView writeFileInCache:data name:localFile];
});
return;
}
PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init];
@ -409,10 +427,34 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100;
}];
}
- (IBAction)onPlusClick:(id)sender {
UILongPressGestureRecognizer *gesture = (UILongPressGestureRecognizer *)sender;
if (gesture.state != UIGestureRecognizerStateBegan) {
// allow only one click once time
return;
}
DTActionSheet *sheet = [[DTActionSheet alloc] initWithTitle:@""];
dispatch_async(dispatch_get_main_queue(), ^{
[sheet addButtonWithTitle:NSLocalizedString(@"Save to Gallery", nil)
block:^() {
LinphoneContent *content = linphone_chat_message_get_file_transfer_information(self.message);
NSString *name = [NSString stringWithUTF8String:linphone_content_get_name(content)];
[ChatConversationView writeMediaToGallery:name fileType:[NSString stringWithUTF8String:linphone_content_get_type(content)?:""]];
}];
[sheet addCancelButtonWithTitle:NSLocalizedString(@"Cancel", nil) block:nil];
[sheet showInView:PhoneMainView.instance.view];
});
}
- (IBAction)onFileClick:(id)sender {
ChatConversationView *view = VIEW(ChatConversationView);
NSString *name = [LinphoneManager getMessageAppDataForKey:@"localfile" inMessage:self.message];
[view openFileWithURL:[view getICloudFileUrl:name]];
if([[NSFileManager defaultManager] fileExistsAtPath:[[LinphoneManager cacheDirectory] stringByAppendingPathComponent:name]]) {
[view openFileWithURL:[ChatConversationView getCacheFileUrl:name]];
} else {
[view openFileWithURL:[view getICloudFileUrl:name]];
}
}
@ -445,17 +487,29 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100;
if (![_messageImageView isLoading]) {
ImageView *view = VIEW(ImageView);
[PhoneMainView.instance changeCurrentView:view.compositeViewDescription];
NSString *localImage = [LinphoneManager getMessageAppDataForKey:@"localimage" inMessage:self.message];
NSString *localFile = [LinphoneManager getMessageAppDataForKey:@"localfile" inMessage:self.message];
NSString *imageName = NULL;
if (localImage && [[NSFileManager defaultManager] fileExistsAtPath:[[LinphoneManager cacheDirectory] stringByAppendingPathComponent:localImage]]) {
imageName = localImage;
} else if (localFile && [[NSFileManager defaultManager] fileExistsAtPath:[[LinphoneManager cacheDirectory] stringByAppendingPathComponent:localFile]]) {
if ([localFile hasSuffix:@"JPG"] || [localFile hasSuffix:@"PNG"] || [localFile hasSuffix:@"jpg"] || [localFile hasSuffix:@"png"]) {
imageName = localFile;
}
}
if (imageName) {
NSData *data = [NSData dataWithContentsOfFile:[[LinphoneManager cacheDirectory] stringByAppendingPathComponent:imageName]];
UIImage *image = [[UIImage alloc] initWithData:data];
if (image)
[view setImage:image];
else
LOGE(@"Can't read image");
return;
}
PHAsset *asset = [_messageImageView asset];
if (!asset) {
NSString *localFile = [LinphoneManager getMessageAppDataForKey:@"localfile" inMessage:self.message];
if ([localFile hasSuffix:@"JPG"] || [localFile hasSuffix:@"PNG"] || [localFile hasSuffix:@"jpg"] || [localFile hasSuffix:@"png"]) {
NSData *data = [NSData dataWithContentsOfURL:[VIEW(ChatConversationView) getICloudFileUrl:localFile]];
UIImage *image = [[UIImage alloc] initWithData:data];
if (image)
[view setImage:image];
else
LOGE(@"Can't read image");
}
return;
}
PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
@ -516,6 +570,7 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100;
} else {
ChatConversationView *view = VIEW(ChatConversationView);
[view.tableController updateEventEntry:self.event];
[view.tableController scrollToBottom:true];
}
}
- (void)onFileTransferRecvUpdate:(NSNotification *)notif {
@ -527,6 +582,7 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100;
} else {
ChatConversationView *view = VIEW(ChatConversationView);
[view.tableController updateEventEntry:self.event];
[view.tableController scrollToBottom:true];
}
}

View file

@ -52,7 +52,7 @@
+ (UIImage *)getImageFromVideoUrl:(NSURL *)url;
- (void)setEvent:(LinphoneEventLog *)event;
- (void)setChatMessage:(LinphoneChatMessage *)message;
- (void)setChatMessageForCbs:(LinphoneChatMessage *)message;
- (void)onDelete;
- (void)onResend;

View file

@ -57,12 +57,13 @@
//[_imdmLabel addGestureRecognizer:resendRecognizer2];
//_imdmLabel.userInteractionEnabled = YES;
self.contentView.userInteractionEnabled = NO;
return self;
}
- (void)dealloc {
[self setEvent:NULL];
[self setChatMessage:NULL];
[self setChatMessageForCbs:NULL];
}
#pragma mark -
@ -76,10 +77,10 @@
LOGE(@"Impossible to create a ChatBubbleText whit a non message event");
return;
}
[self setChatMessage:linphone_event_log_get_chat_message(event)];
[self setChatMessageForCbs:linphone_event_log_get_chat_message(event)];
}
- (void)setChatMessage:(LinphoneChatMessage *)amessage {
- (void)setChatMessageForCbs:(LinphoneChatMessage *)amessage {
if (!amessage || amessage == _message) {
return;
}
@ -275,74 +276,33 @@
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 *localVideo = [LinphoneManager getMessageAppDataForKey:@"localvideo" inMessage:_message];
NSString *localFile = [LinphoneManager getMessageAppDataForKey:@"localfile" inMessage:_message];
/*FileTransferDelegate *thiz = [FileTransferDelegate messageDelegate:_message];
if (thiz) {
[thiz stop]
}*/
[self onDelete];
if(localImage){
ChatConversationTableView *tableView = VIEW(ChatConversationView).tableController;
UIImage *img = [tableView.imagesInChatroom objectForKey:localImage];
if (img) {
dispatch_async(dispatch_get_main_queue(), ^ {
[_chatRoomDelegate startImageUpload:img assetId:localImage withQuality:(uploadQuality ? [uploadQuality floatValue] : 0.9)];
});
} else {
PHFetchResult<PHAsset *> *assets = [LinphoneManager getPHAssets:localImage];
if (![assets firstObject])
return;
PHAsset *asset = [assets firstObject];
if (asset.mediaType != PHAssetMediaTypeImage)
return;
PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
options.synchronous = TRUE;
[[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeDefault options:options
resultHandler:^(UIImage *image, NSDictionary * info) {
if (image) {
dispatch_async(dispatch_get_main_queue(),
^(void) {
[_chatRoomDelegate startImageUpload:img assetId:localImage withQuality:(uploadQuality ? [uploadQuality floatValue] : 0.9)];
});
} else {
LOGE(@"Can't read image");
}
}];
}
} else if (localVideo) {
PHFetchResult<PHAsset *> *assets = [PHAsset fetchAssetsWithLocalIdentifiers:[NSArray arrayWithObject:localVideo] options:nil];
if (![assets firstObject])
return;
PHAsset *asset = [assets firstObject];
if (asset.mediaType != PHAssetMediaTypeVideo)
return;
PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init];
options.version = PHImageRequestOptionsVersionCurrent;
options.deliveryMode = PHVideoRequestOptionsDeliveryModeAutomatic;
[[PHImageManager defaultManager] requestAVAssetForVideo:asset options:options resultHandler:^(AVAsset * _Nullable asset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) {
AVURLAsset *urlAsset = (AVURLAsset *)asset;
NSURL *url = urlAsset.URL;
NSData *data = [NSData dataWithContentsOfURL:url];
dispatch_async(dispatch_get_main_queue(),
^(void) {
[_chatRoomDelegate startFileUpload:data assetId:localVideo];
});
}];
} else if (localFile) {
NSData *data = [NSData dataWithContentsOfURL:[VIEW(ChatConversationView) getICloudFileUrl:localFile]];
[_chatRoomDelegate startFileUpload:data withName:localFile];
}
if(localImage){
dispatch_async(dispatch_get_main_queue(), ^ {
[_chatRoomDelegate resendFile:[ChatConversationView getCacheFileData:localImage] withName:localImage type:@"image" key:@"localimage" message:self.textMessage];
});
} else if (localVideo) {
dispatch_async(dispatch_get_main_queue(), ^ {
[_chatRoomDelegate resendFile:[ChatConversationView getCacheFileData:localVideo] withName:localVideo type:@"video" key:@"localvideo" message:self.textMessage];
});
} else if (localFile) {
dispatch_async(dispatch_get_main_queue(), ^ {
[_chatRoomDelegate resendFile:[ChatConversationView getCacheFileData:localFile] withName:localFile type:@"image" key:@"localfile" message:self.textMessage];
});
}
} else {
[self onDelete];
[self onDelete];
double delayInSeconds = 0.4;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void) {
[_chatRoomDelegate resendChat:self.textMessage withExternalUrl:nil];
[_chatRoomDelegate resendChat:self.textMessage withExternalUrl:nil];
});
}
}
@ -352,6 +312,7 @@ static void message_status(LinphoneChatMessage *msg, LinphoneChatMessageState st
LinphoneEventLog *event = (LinphoneEventLog *)linphone_chat_message_cbs_get_user_data(linphone_chat_message_get_callbacks(msg));
ChatConversationView *view = VIEW(ChatConversationView);
[view.tableController updateEventEntry:event];
[view.tableController scrollToBottom:true];
}
static void participant_imdn_status(LinphoneChatMessage* msg, const LinphoneParticipantImdnState *state) {
@ -403,6 +364,14 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100;
return [self ViewHeightForMessageText:chat withWidth:width textForImdn:nil];
}
+ (CGSize)ViewHeightForFile:(int)width {
CGSize fileSize = CGSizeMake(230, 50);
CGSize size = [self getMediaMessageSizefromOriginalSize:fileSize withWidth:width];
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;
}
+ (CGSize)ViewHeightForMessageText:(LinphoneChatMessage *)chat withWidth:(int)width textForImdn:(NSString *)imdnText{
NSString *messageText = [UIChatBubbleTextCell TextMessageForChat:chat];
static UIFont *messageFont = nil;
@ -442,53 +411,75 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100;
font:messageFont];
size.height += textSize.height;
}
if(localFile) {
UIImage *image = nil;
NSString *type = [NSString stringWithUTF8String:linphone_content_get_type(fileContent)];
if ([type isEqualToString:@"video"]) {
image = [self getImageFromVideoUrl:[VIEW(ChatConversationView) getICloudFileUrl:localFile]];
} else if ([localFile hasSuffix:@"JPG"] || [localFile hasSuffix:@"PNG"] || [localFile hasSuffix:@"jpg"] || [localFile hasSuffix:@"png"]) {
NSData *data = [NSData dataWithContentsOfURL:[VIEW(ChatConversationView) getICloudFileUrl:localFile]];
image = [[UIImage alloc] initWithData:data];
}
if (image) {
size = [self getMediaMessageSizefromOriginalSize:image.size withWidth:width];
// add size for message text
size.height += textSize.height;
size.width = MAX(textSize.width, size.width);
} else {
CGSize fileSize = CGSizeMake(230, 50);
size = [self getMediaMessageSizefromOriginalSize:fileSize withWidth:width];
}
} else {
if (!localImage && !localVideo) {
//We are loading the image
return CGSizeMake(CELL_MIN_WIDTH + CELL_MESSAGE_X_MARGIN, CELL_MIN_HEIGHT + CELL_MESSAGE_Y_MARGIN + textSize.height + 20);
}
PHFetchResult<PHAsset *> *assets;
if(localImage)
assets = [LinphoneManager getPHAssets:localImage];
else
assets = [PHAsset fetchAssetsWithLocalIdentifiers:[NSArray arrayWithObject:localVideo] options:nil];
if (![assets firstObject]) {
return CGSizeMake(CELL_MIN_WIDTH, CELL_MIN_WIDTH + CELL_MESSAGE_Y_MARGIN + textSize.height);
} else {
PHAsset *asset = [assets firstObject];
CGSize originalImageSize = CGSizeMake([asset pixelWidth], [asset pixelHeight]);
size = [self getMediaMessageSizefromOriginalSize:originalImageSize withWidth:width];
// add size for message text
size.height += textSize.height;
size.width = MAX(textSize.width, size.width);
}
}
}
CGSize originalImageSize = CGSizeMake(230, 50);
if (localFile) {
UIImage *image = nil;
NSString *type = [NSString stringWithUTF8String:linphone_content_get_type(fileContent)];
NSString *filePath = [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:localFile];
if ([type isEqualToString:@"video"]) {
if ([[NSFileManager defaultManager] fileExistsAtPath: filePath]) {
image = [self getImageFromVideoUrl:[ChatConversationView getCacheFileUrl:localFile]];
} else {
image = [self getImageFromVideoUrl:[VIEW(ChatConversationView) getICloudFileUrl:localFile]];
}
} else if ([localFile hasSuffix:@"JPG"] || [localFile hasSuffix:@"PNG"] || [localFile hasSuffix:@"jpg"] || [localFile hasSuffix:@"png"]) {
if ([[NSFileManager defaultManager] fileExistsAtPath: filePath]) {
NSData *data = [NSData dataWithContentsOfFile:filePath];
image = [[UIImage alloc] initWithData:data];
} else {
NSData *data = [NSData dataWithContentsOfURL:[VIEW(ChatConversationView) getICloudFileUrl:localFile]];
image = [[UIImage alloc] initWithData:data];
}
} else {
return [self ViewHeightForFile:width];
}
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;
originalImageSize = image.size;
} else {
if (!localImage && !localVideo) {
//We are loading the image
return CGSizeMake(CELL_MIN_WIDTH + CELL_MESSAGE_X_MARGIN, CELL_MIN_HEIGHT + CELL_MESSAGE_Y_MARGIN + textSize.height + 20);
}
if (localImage && [[NSFileManager defaultManager] fileExistsAtPath:[[LinphoneManager cacheDirectory] stringByAppendingPathComponent:localImage]]) {
NSData* data = [ChatConversationView getCacheFileData:localImage];
UIImage *image = [[UIImage alloc] initWithData:data];
if (!image) {
return [self ViewHeightForFile:width];
}
originalImageSize = image.size;
} else if (localVideo && [[NSFileManager defaultManager] fileExistsAtPath:[[LinphoneManager cacheDirectory] stringByAppendingPathComponent:localVideo]]) {
UIImage *image = [UIChatBubbleTextCell getImageFromVideoUrl:[ChatConversationView getCacheFileUrl:localVideo]];
if (!image) {
return [self ViewHeightForFile:width];
}
originalImageSize = image.size;
} else {
// support previous versions
PHFetchResult<PHAsset *> *assets;
if(localImage)
assets = [LinphoneManager getPHAssets:localImage];
else
assets = [PHAsset fetchAssetsWithLocalIdentifiers:[NSArray arrayWithObject:localVideo] options:nil];
if (![assets firstObject]) {
return CGSizeMake(CELL_MIN_WIDTH, CELL_MIN_WIDTH + CELL_MESSAGE_Y_MARGIN + textSize.height);
} else {
PHAsset *asset = [assets firstObject];
originalImageSize = CGSizeMake([asset pixelWidth], [asset pixelHeight]);
}
}
}
size = [self getMediaMessageSizefromOriginalSize:originalImageSize withWidth:width];
// add size for message text
size.height += textSize.height;
size.width = MAX(textSize.width, size.width);
}
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;
}
+ (CGSize)ViewSizeForMessage:(LinphoneChatMessage *)chat withWidth:(int)width {

View file

@ -67,7 +67,7 @@ INIT_WITH_COMMON_CF {
LinphoneCallParams *call_params = linphone_core_create_call_params(LC,call);
linphone_call_params_enable_video(call_params, FALSE);
linphone_core_update_call(LC, call, call_params);
linphone_call_params_destroy(call_params);
linphone_call_params_unref(call_params);
} else {
LOGW(@"Cannot toggle video button, because no current call");
}

View file

@ -51,6 +51,7 @@
#import "UIConfirmationDialog.h"
#import "Utils.h"
#import "LaunchScreen.h"
#import "DevicesListView.h"
#define DYNAMIC_CAST(x, cls) \
({ \

View file

@ -174,9 +174,9 @@ extension ProviderDelegate: CXProviderDelegate {
Log.directLog(BCTBX_LOG_MESSAGE, text: "CallKit: answer call with call-id: \(String(describing: callId)) and UUID: \(uuid.description).")
let call = CallManager.instance().callByCallId(callId: callId)
CallManager.instance().lc?.configureAudioSession()
if (call == nil || call?.state != Call.State.IncomingReceived) {
// The application is not yet registered or the call is not yet received, mark the call as accepted. The audio session must be configured here.
CallManager.configAudioSession(audioSession: AVAudioSession.sharedInstance())
callInfo?.accepted = true
callInfos.updateValue(callInfo!, forKey: uuid)
CallManager.instance().providerDelegate.endCallNotExist(uuid: uuid, timeout: .now() + 10)
@ -242,6 +242,7 @@ extension ProviderDelegate: CXProviderDelegate {
action.fail()
}
CallManager.instance().lc?.configureAudioSession()
try CallManager.instance().doCall(addr: addr!, isSas: callInfo?.sasEnabled ?? false)
} catch {
Log.directLog(BCTBX_LOG_ERROR, text: "CallKit: Call started failed because \(error)")

View file

@ -84,6 +84,10 @@
NSArray *parsedName = [LinphoneUtils parseRecordingName:file];
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:@"EEEE, MMM d, yyyy"];
if ([parsedName count] < 2) {
LOGW(@"Can not parse this recoding file: %@", file);
continue;
}
NSString *dayPretty = [dateFormat stringFromDate:[parsedName objectAtIndex:1]];
NSMutableArray *recOfDay = [recordings objectForKey:dayPretty];
if (recOfDay) {

View file

@ -23,12 +23,14 @@
@interface FileTransferDelegate : NSObject
- (void)upload:(UIImage *)image withassetId:(NSString *)phAssetId forChatRoom:(LinphoneChatRoom *)chatRoom withQuality:(float)quality;
- (void)uploadData:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom type:(NSString *)type subtype:(NSString *)subtype name:(NSString *)name key:(NSString *)key;
- (void)uploadImage:(UIImage *)image forChatRoom:(LinphoneChatRoom *)chatRoom withQuality:(float)quality;
- (void)uploadFile:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom withName:(NSString *)name;
- (void)uploadVideo:(NSData *)data withassetId:(NSString *)phAssetId forChatRoom:(LinphoneChatRoom *)chatRoom;
- (void)cancel;
- (BOOL)download:(LinphoneChatMessage *)message;
- (void)stopAndDestroy;
+ (FileTransferDelegate *)messageDelegate:(LinphoneChatMessage *)message;
@property() LinphoneChatMessage *message;
@property() NSString *text;

View file

@ -43,189 +43,41 @@
return nil;
}
static void linphone_iphone_file_transfer_recv(LinphoneChatMessage *message, const LinphoneContent *content,
const LinphoneBuffer *buffer) {
static void file_transfer_progress_indication_recv(LinphoneChatMessage *message, LinphoneContent* content, size_t offset, size_t total) {
FileTransferDelegate *thiz = [FileTransferDelegate messageDelegate:message];
size_t size = linphone_buffer_get_size(buffer);
if (!thiz.data) {
thiz.data = [[NSMutableData alloc] initWithCapacity:linphone_content_get_file_size(content)];
}
if (offset == total) {
NSString *name = [NSString stringWithUTF8String: linphone_content_get_name(content) ? : ""];
LOGI(@"Transfer of %@ (%d bytes): download finished", name, total);
NSString *fileType = [NSString stringWithUTF8String:linphone_content_get_type(content)];
NSString *key = [ChatConversationView getKeyFromFileType:fileType fileName:name];
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)];
ChatConversationView *view = VIEW(ChatConversationView);
void (^block)(void)= ^ {
if ([fileType isEqualToString:@"image"]) {
// we're finished, save the image and update the message
UIImage *image = [UIImage imageWithData:thiz.data];
if (!image) {
[view showFileDownloadError];
[thiz stopAndDestroy];
return;
}
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];
__block PHObjectPlaceholder *placeHolder;
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
PHAssetCreationRequest *request = [PHAssetCreationRequest creationRequestForAssetFromImage:image];
placeHolder = [request placeholderForCreatedAsset];
} completionHandler:^(BOOL success, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
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"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {}];
[errView addAction:defaultAction];
[PhoneMainView.instance presentViewController:errView animated:YES completion:nil];
} else {
LOGI(@"Image saved to [%@]", [placeHolder localIdentifier]);
[LinphoneManager setValueInMessageAppData:[placeHolder localIdentifier]
forKey:@"localimage"
inMessage:message];
}
[NSNotificationCenter.defaultCenter
postNotificationName:kLinphoneFileTransferRecvUpdate
object:thiz
userInfo:@{
@"state" : @(LinphoneChatMessageStateDelivered), // we dont want to
// trigger
// FileTransferDone here
@"image" : image,
@"progress" : @(1.f),
}];
[thiz stopAndDestroy];
CFRelease((__bridge CFTypeRef)thiz);
});
}];
} else if([fileType isEqualToString:@"video"]) {
CFBridgingRetain(thiz);
[[LinphoneManager.instance fileTransferDelegates] removeObject:thiz];
NSString *name =[NSString stringWithUTF8String:linphone_content_get_name(content)];
NSString *filePath = [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:name];
[[NSFileManager defaultManager] createFileAtPath:filePath
contents:thiz.data
attributes:nil];
// 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:@"localvideo" inMessage:message];
__block PHObjectPlaceholder *placeHolder;
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
PHAssetCreationRequest *request = [PHAssetCreationRequest creationRequestForAssetFromVideoAtFileURL:[NSURL fileURLWithPath:filePath]];
placeHolder = [request placeholderForCreatedAsset];
} completionHandler:^(BOOL success, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
LOGE(@"Cannot save video data downloaded [%@]", [error localizedDescription]);
[LinphoneManager setValueInMessageAppData:nil forKey:@"localvideo" inMessage:message];
UIAlertController *errView = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Transfer error", nil)
message:NSLocalizedString(@"Cannot write video to photo library",
nil)
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {}];
[errView addAction:defaultAction];
[PhoneMainView.instance presentViewController:errView animated:YES completion:nil];
} else {
LOGI(@"video saved to [%@]", [placeHolder localIdentifier]);
[LinphoneManager setValueInMessageAppData:[placeHolder localIdentifier]
forKey:@"localvideo"
inMessage:message];
}
[NSNotificationCenter.defaultCenter
postNotificationName:kLinphoneFileTransferRecvUpdate
object:thiz
userInfo:@{
@"state" : @(LinphoneChatMessageStateDelivered), // we dont want to
// trigger
// FileTransferDone here
@"progress" : @(1.f),
}];
[thiz stopAndDestroy];
CFRelease((__bridge CFTypeRef)thiz);
});
}];
}
};
// When you save an image or video to a photo library, make sure that it is allowed. Otherwise, there will be a backup error.
if ([fileType isEqualToString:@"image"] || [fileType isEqualToString:@"video"]) {
if ([PHPhotoLibrary authorizationStatus] == PHAuthorizationStatusAuthorized) {
block();
} else {
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
dispatch_async(dispatch_get_main_queue(), ^{
if ([PHPhotoLibrary authorizationStatus] == PHAuthorizationStatusAuthorized) {
block();
} else {
[[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Photo's permission", nil) message:NSLocalizedString(@"Photo not authorized", nil) delegate:nil cancelButtonTitle:nil otherButtonTitles:@"Continue", nil] show];
[thiz stopAndDestroy];
}
});
}];
}
} else {
[[LinphoneManager.instance fileTransferDelegates] removeObject:thiz];
NSString *key = @"localfile" ;
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(), ^{
if([view writeFileInICloud:thiz.data fileURL:[view getICloudFileUrl:name]]) {
[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];
dispatch_async(dispatch_get_main_queue(), ^{
[LinphoneManager setValueInMessageAppData:name
forKey:key
inMessage:message];
dispatch_async(dispatch_get_main_queue(), ^{
if ([ConfigManager.instance lpConfigBoolForKeyWithKey:@"auto_write_to_gallery_preference"]) {
[ChatConversationView writeMediaToGallery:name fileType:fileType];
}
});
});
} else {
LOGD(@"Transfer of %s (%d bytes): already %ld recv", linphone_content_get_name(content),
total, offset);
[NSNotificationCenter.defaultCenter
postNotificationName:kLinphoneFileTransferRecvUpdate
object:thiz
userInfo:@{
@"state" : @(linphone_chat_message_get_state(message)),
@"progress" : @([thiz.data length] * 1.f / linphone_content_get_file_size(content)),
@"progress" : @(offset * 1.f / total),
}];
}
}
static LinphoneBuffer *linphone_iphone_file_transfer_send(LinphoneChatMessage *message, const LinphoneContent *content,
size_t offset, size_t size) {
static void file_transfer_progress_indication_send(LinphoneChatMessage *message, LinphoneContent* content, size_t offset, size_t total) {
FileTransferDelegate *thiz = [FileTransferDelegate messageDelegate:message];
size_t total = thiz.data.length;
if (thiz.data) {
if (total) {
size_t remaining = total - offset;
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:@{
@ -237,109 +89,85 @@ static LinphoneBuffer *linphone_iphone_file_transfer_send(LinphoneChatMessage *m
[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneFileTransferSendUpdate
object:thiz
userInfo:dict];
LinphoneBuffer *buffer = NULL;
@try {
buffer = linphone_buffer_new_from_data([thiz.data subdataWithRange:NSMakeRange(offset, size)].bytes, size);
} @catch (NSException *exception) {
LOGE(@"Exception: %@", exception);
}
// this is the last time we will be notified, so destroy ourselve
if (remaining <= size) {
if (offset == total) {
LOGI(@"Upload ended");
linphone_chat_message_cbs_set_file_transfer_send(linphone_chat_message_get_callbacks(thiz.message), NULL);
thiz.message = NULL;
[thiz stopAndDestroy];
//workaround fix : avoid chatconversationtableview scrolling
[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneFileTransferSendUpdate
object:thiz
userInfo:@{@"state" : @(LinphoneChatMessageStateDelivered),
}];
}
return buffer;
} else {
LOGE(@"Transfer of %s (%d bytes): %d Error - no upload data in progress!", linphone_content_get_name(content),
total, offset);
}
return NULL;
}
- (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);
BOOL isOneToOneChat = linphone_chat_room_get_capabilities(chatRoom) & LinphoneChatRoomCapabilitiesOneToOne;
if (!isOneToOneChat && ![_text isEqualToString:@""])
linphone_chat_message_add_text_content(_message, [_text UTF8String]);
linphone_content_unref(content);
linphone_chat_message_cbs_set_file_transfer_send(linphone_chat_message_get_callbacks(_message),
linphone_iphone_file_transfer_send);
- (void)uploadData:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom type:(NSString *)type subtype:(NSString *)subtype name:(NSString *)name key:(NSString *)key{
if ([[LinphoneManager.instance fileTransferDelegates] containsObject:self]) {
LOGW(@"fileTransferDelegates has already added %p", self);
return;
}
[LinphoneManager.instance.fileTransferDelegates addObject:self];
// 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_message_send(_message);
LinphoneContent *content = linphone_core_create_content(linphone_chat_room_get_core(chatRoom));
linphone_content_set_type(content, [type UTF8String]);
linphone_content_set_subtype(content, [subtype UTF8String]);
linphone_content_set_name(content, [name UTF8String]);
linphone_content_set_file_path(content, [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:name].UTF8String);
_message = linphone_chat_room_create_file_transfer_message(chatRoom, content);
BOOL isOneToOneChat = linphone_chat_room_get_capabilities(chatRoom) & LinphoneChatRoomCapabilitiesOneToOne;
if (!isOneToOneChat && (_text!=nil && ![_text isEqualToString:@""]))
linphone_chat_message_add_text_content(_message, [_text UTF8String]);
linphone_content_unref(content);
linphone_chat_message_cbs_set_file_transfer_progress_indication(linphone_chat_message_get_callbacks(_message), file_transfer_progress_indication_send);
[LinphoneManager setValueInMessageAppData:name forKey:key inMessage:_message];
LOGI(@"%p Uploading content from message %p", self, _message);
linphone_chat_message_send(_message);
}
- (void)upload:(UIImage *)image withassetId:(NSString *)phAssetId forChatRoom:(LinphoneChatRoom *)chatRoom withQuality:(float)quality {
NSString *name = [NSString stringWithFormat:@"%li-%f.jpg", (long)image.hash, [NSDate timeIntervalSinceReferenceDate]];
if (phAssetId)
[self uploadData:UIImageJPEGRepresentation(image, quality) forChatRoom:chatRoom type:@"image" subtype:@"jpeg" name:name key:@"localimage" keyData:phAssetId 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)uploadImage:(UIImage *)image forChatRoom:(LinphoneChatRoom *)chatRoom withQuality:(float)quality {
NSString *name = [NSString stringWithFormat:@"%li-%f.jpg", (long)image.hash, [NSDate timeIntervalSinceReferenceDate]];
NSData *data = UIImageJPEGRepresentation(image, quality);
[ChatConversationView writeFileInCache:data name:name];
[self uploadData:data forChatRoom:chatRoom type:@"image" subtype:@"jpg" name:name key:@"localimage"];
}
- (void)uploadVideo:(NSData *)data withassetId:(NSString *)phAssetId forChatRoom:(LinphoneChatRoom *)chatRoom {
NSString *name = [NSString stringWithFormat:@"IMG-%f.MOV", [NSDate timeIntervalSinceReferenceDate]];
if (phAssetId)
[self uploadData:data forChatRoom:chatRoom type:@"video" subtype:nil name:name key:@"localvideo" keyData:phAssetId qualityData:nil];
else
[self uploadData:data forChatRoom:chatRoom type:@"video" subtype:nil name:name key:@"localvideo" keyData:@"ending..." qualityData:nil];
NSString *name = [NSString stringWithFormat:@"IMG-%f.MOV", [NSDate timeIntervalSinceReferenceDate]];
[ChatConversationView writeFileInCache:data name:name];
[self uploadData:data forChatRoom:chatRoom type:@"video" subtype:@"mov" name:name key:@"localvideo"];
}
- (void)uploadFile:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom withName:(NSString *)name {
// we will write local files into ours folder of icloud
ChatConversationView *view = VIEW(ChatConversationView);
NSURL *url = [view getICloudFileUrl:name];
if ([view writeFileInICloud:data fileURL:url]) {
AVAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil];
if ([[asset tracksWithMediaType:AVMediaTypeVideo] count] > 0) {
// if it's a video
[self uploadData:data forChatRoom:chatRoom type:@"video" subtype:nil name:name key:@"localfile" keyData:name qualityData:nil];
} else {
[self uploadData:data forChatRoom:chatRoom type:@"file" subtype:nil name:name key:@"localfile" keyData:name qualityData:nil];
}
}
[ChatConversationView writeFileInCache:data name:name];
NSURL *url = [ChatConversationView getCacheFileUrl:name];
AVAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil];
NSString *fileType = [[asset tracksWithMediaType:AVMediaTypeVideo] count] > 0 ? @"video" : @"file";
NSString *key = [ChatConversationView getKeyFromFileType:fileType fileName:name];
[self uploadData:data forChatRoom:chatRoom type:fileType subtype:name.lastPathComponent name:name key:key];
}
- (BOOL)download:(LinphoneChatMessage *)message {
if ([[LinphoneManager.instance fileTransferDelegates] containsObject:self]) {
LOGW(@"fileTransferDelegates has already added %p", self);
return FALSE;
}
[[LinphoneManager.instance fileTransferDelegates] addObject:self];
_message = message;
const char *url = linphone_chat_message_get_external_body_url(_message);
LOGI(@"%p Downloading content in %p from %s", self, message, url);
LinphoneContent *content = linphone_chat_message_get_file_transfer_information(_message);
if (content == nil) return FALSE;
if (url == nil)
return FALSE;
LOGI(@"%p Downloading content in %p ", self, message);
linphone_chat_message_cbs_set_file_transfer_recv(linphone_chat_message_get_callbacks(_message),
linphone_iphone_file_transfer_recv);
linphone_chat_message_download_file(_message);
linphone_chat_message_cbs_set_file_transfer_progress_indication(linphone_chat_message_get_callbacks(_message), file_transfer_progress_indication_recv);
linphone_content_set_file_path(content, [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:[NSString stringWithUTF8String:linphone_content_get_name(content)]].UTF8String);
linphone_chat_message_download_content(_message, content);
return TRUE;
}
@ -350,8 +178,7 @@ static LinphoneBuffer *linphone_iphone_file_transfer_send(LinphoneChatMessage *m
LinphoneChatMessage *msg = _message;
_message = NULL;
LOGI(@"%p Cancelling transfer from %p", self, msg);
linphone_chat_message_cbs_set_file_transfer_send(linphone_chat_message_get_callbacks(msg), NULL);
linphone_chat_message_cbs_set_file_transfer_recv(linphone_chat_message_get_callbacks(msg), NULL);
linphone_chat_message_cbs_set_file_transfer_progress_indication(linphone_chat_message_get_callbacks(msg), NULL);
// when we cancel file transfer, this will automatically trigger NotDelivered callback... recalling ourself a
// second time so we have to unset message BEFORE calling this
linphone_chat_message_cancel_file_transfer(msg);

View file

@ -63,7 +63,7 @@
}
+ (void)directLog:(OrtpLogLevel)level text:(NSString *)text {
bctbx_log(BCTBX_LOG_DOMAIN, level, "%s", text.cString);
bctbx_log(BCTBX_LOG_DOMAIN, level, "%s", [text cStringUsingEncoding:NSUTF8StringEncoding]);
}
#pragma mark - Logs Functions callbacks

View file

@ -0,0 +1,18 @@
/* Class = "UIButton"; accessibilityLabel = "Select all"; ObjectID = "16S-9G-2cb"; */
"16S-9G-2cb.accessibilityLabel" = "Select all";
/* Class = "UIButton"; accessibilityLabel = "Edit"; ObjectID = "CWx-9g-0JG"; */
"CWx-9g-0JG.accessibilityLabel" = "Edit";
/* Class = "UIButton"; accessibilityLabel = "Delete all"; ObjectID = "CqG-tB-maQ"; */
"CqG-tB-maQ.accessibilityLabel" = "Delete all";
/* Class = "UIButton"; accessibilityLabel = "Back"; ObjectID = "rAc-tI-AVp"; */
"rAc-tI-AVp.accessibilityLabel" = "Back";
/* Class = "UIButton"; accessibilityLabel = "Delete all"; ObjectID = "zDs-pW-vyA"; */
"zDs-pW-vyA.accessibilityLabel" = "Delete all";
/* Class = "UILabel"; text = "No recording found"; ObjectID = "zXd-Ic-rwm"; */
"zXd-Ic-rwm.text" = "No recording found";

View file

@ -5,7 +5,7 @@ source "https://github.com/CocoaPods/Specs.git"
def all_pods
if ENV['PODFILE_PATH'].nil?
pod 'linphone-sdk', '4.4.3'
pod 'linphone-sdk', '~> 4.5.0-beta'
else
pod 'linphone-sdk', :path => ENV['PODFILE_PATH'] # local sdk
end

View file

@ -15,6 +15,7 @@
<entry name="realm" overwrite="true">sip.linphone.org</entry>
<entry name="nat_policy_ref" overwrite="true">nat_policy_default_values</entry>
<entry name="push_notification_allowed" overwrite="true">1</entry>
<entry name="conference_factory_uri" overwrite="true">sip:conference-factory@sip.linphone.org</entry>
</section>
<section name="nat_policy_default_values">

View file

@ -15,6 +15,7 @@
<entry name="realm" overwrite="true">sip.linphone.org</entry>
<entry name="nat_policy_ref" overwrite="true">nat_policy_default_values</entry>
<entry name="push_notification_allowed" overwrite="true">1</entry>
<entry name="conference_factory_uri" overwrite="true">sip:conference-factory@sip.linphone.org</entry>
</section>
<section name="nat_policy_default_values">

View file

@ -26,6 +26,16 @@
<string>Customize</string>
</array>
</dict>
<dict>
<key>DefaultValue</key>
<true/>
<key>Key</key>
<string>auto_write_to_gallery_mode</string>
<key>Title</key>
<string>Auto write to gallery</string>
<key>Type</key>
<string>PSToggleSwitchSpecifier</string>
</dict>
<dict>
<key>Key</key>
<string>auto_download_incoming_files_max_size</string>

View file

@ -103,6 +103,7 @@
615A28402180A2620060F920 /* invite_linphone@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 615A283F2180A2620060F920 /* invite_linphone@2x.png */; };
615A28422180C0870060F920 /* recording.png in Resources */ = {isa = PBXBuildFile; fileRef = 615A28412180C0820060F920 /* recording.png */; };
615A28442180C0900060F920 /* recording@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 615A28432180C08F0060F920 /* recording@2x.png */; };
617B4A60260A2B7800A87337 /* RecordingsListView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 617B4A62260A2B7800A87337 /* RecordingsListView.xib */; };
6180D6FE21EE41A800AD9CB6 /* QuickLook.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6180D6FD21EE41A800AD9CB6 /* QuickLook.framework */; };
61AE364F20C00B370089D9D3 /* ShareViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 61AE364E20C00B370089D9D3 /* ShareViewController.m */; };
61AE365220C00B370089D9D3 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 61AE365020C00B370089D9D3 /* MainInterface.storyboard */; };
@ -678,7 +679,6 @@
CF1DE92D210A0F5D00A0A97E /* UILinphoneAudioPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = CF1DE924210A0F5A00A0A97E /* UILinphoneAudioPlayer.m */; };
CF1DE92E210A0F5D00A0A97E /* UILinphoneAudioPlayer.xib in Resources */ = {isa = PBXBuildFile; fileRef = CF1DE92B210A0F5B00A0A97E /* UILinphoneAudioPlayer.xib */; };
CF7602D7210867E800749F76 /* RecordingsListView.m in Sources */ = {isa = PBXBuildFile; fileRef = CF7602D5210867E800749F76 /* RecordingsListView.m */; };
CF7602D8210867E800749F76 /* RecordingsListView.xib in Resources */ = {isa = PBXBuildFile; fileRef = CF7602D6210867E800749F76 /* RecordingsListView.xib */; };
CF7602E221086EB200749F76 /* RecordingsListTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = CF7602E021086EB200749F76 /* RecordingsListTableView.m */; };
CF7602E72108759A00749F76 /* UIRecordingCell.m in Sources */ = {isa = PBXBuildFile; fileRef = CF7602E52108759A00749F76 /* UIRecordingCell.m */; };
CF7602E82108759A00749F76 /* UIRecordingCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = CF7602E62108759A00749F76 /* UIRecordingCell.xib */; };
@ -959,6 +959,8 @@
615A283F2180A2620060F920 /* invite_linphone@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "invite_linphone@2x.png"; sourceTree = "<group>"; };
615A28412180C0820060F920 /* recording.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = recording.png; sourceTree = "<group>"; };
615A28432180C08F0060F920 /* recording@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "recording@2x.png"; sourceTree = "<group>"; };
617B4A61260A2B7800A87337 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/RecordingsListView.xib; sourceTree = "<group>"; };
617B4A64260A2B8500A87337 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/RecordingsListView.strings; sourceTree = "<group>"; };
6180D6FD21EE41A800AD9CB6 /* QuickLook.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuickLook.framework; path = System/Library/Frameworks/QuickLook.framework; sourceTree = SDKROOT; };
6187B1B524B3271500D580FB /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/AboutView.strings; sourceTree = "<group>"; };
6187B1B624B3271500D580FB /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/AssistantLinkView.strings; sourceTree = "<group>"; };
@ -1715,7 +1717,6 @@
CF1DE92C210A0F5C00A0A97E /* UILinphoneAudioPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UILinphoneAudioPlayer.h; sourceTree = "<group>"; };
CF7602D4210867E800749F76 /* RecordingsListView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RecordingsListView.h; sourceTree = "<group>"; };
CF7602D5210867E800749F76 /* RecordingsListView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RecordingsListView.m; sourceTree = "<group>"; };
CF7602D6210867E800749F76 /* RecordingsListView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = RecordingsListView.xib; sourceTree = "<group>"; };
CF7602DF21086EB100749F76 /* RecordingsListTableView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RecordingsListTableView.h; sourceTree = "<group>"; };
CF7602E021086EB200749F76 /* RecordingsListTableView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RecordingsListTableView.m; sourceTree = "<group>"; };
CF7602E42108759A00749F76 /* UIRecordingCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UIRecordingCell.h; sourceTree = "<group>"; };
@ -2100,7 +2101,7 @@
CF7602E021086EB200749F76 /* RecordingsListTableView.m */,
CF7602D4210867E800749F76 /* RecordingsListView.h */,
CF7602D5210867E800749F76 /* RecordingsListView.m */,
CF7602D6210867E800749F76 /* RecordingsListView.xib */,
617B4A62260A2B7800A87337 /* RecordingsListView.xib */,
D35E759C159460B50066B1C1 /* SettingsView.h */,
D35E759D159460B50066B1C1 /* SettingsView.m */,
636316D61A1DEC650009B839 /* SettingsView.xib */,
@ -3498,7 +3499,7 @@
8CE24F4C1F8234A30077AC0A /* next_default@2x.png in Resources */,
244523B11E8266CC0037A187 /* chat_read.png in Resources */,
61AEBEC62191E47500F35E7F /* chevron_list_close.png in Resources */,
CF7602D8210867E800749F76 /* RecordingsListView.xib in Resources */,
617B4A60260A2B7800A87337 /* RecordingsListView.xib in Resources */,
639E9CAC1C0DB80300019A75 /* UIContactDetailsCell.xib in Resources */,
633FEE511D3CD5590014B822 /* deselect_all@2x.png in Resources */,
8CF25D951F9F336100BEA0C1 /* check_unselected@2x.png in Resources */,
@ -4359,6 +4360,15 @@
name = ShopView.xib;
sourceTree = "<group>";
};
617B4A62260A2B7800A87337 /* RecordingsListView.xib */ = {
isa = PBXVariantGroup;
children = (
617B4A61260A2B7800A87337 /* Base */,
617B4A64260A2B8500A87337 /* fr */,
);
name = RecordingsListView.xib;
sourceTree = "<group>";
};
61AE365020C00B370089D9D3 /* MainInterface.storyboard */ = {
isa = PBXVariantGroup;
children = (
@ -4875,7 +4885,7 @@
CODE_SIGN_STYLE = Automatic;
COMPRESS_PNG_FILES = NO;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 19;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = Z2V957B3D6;
ENABLE_BITCODE = NO;
@ -4904,14 +4914,14 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)";
LINK_WITH_STANDARD_LIBRARIES = YES;
MARKETING_VERSION = 4.3.2;
MARKETING_VERSION = 4.4.0;
OTHER_CFLAGS = (
"-DBCTBX_LOG_DOMAIN=\\\"ios\\\"",
"-DCHECK_VERSION_UPDATE=FALSE",
"-DENABLE_QRCODE=TRUE",
"-DENABLE_SMS_INVITE=TRUE",
"$(inherited)",
"-DLINPHONE_SDK_VERSION=\\\"4.4.3\\\"",
"-DLINPHONE_SDK_VERSION=\\\"4.5.0-beta.31+1a68848\\\"",
);
OTHER_SWIFT_FLAGS = "$(inherited)";
PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone;
@ -5001,7 +5011,7 @@
CODE_SIGN_STYLE = Automatic;
COMPRESS_PNG_FILES = NO;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 19;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = Z2V957B3D6;
ENABLE_BITCODE = NO;
@ -5027,14 +5037,14 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)";
LINK_WITH_STANDARD_LIBRARIES = YES;
MARKETING_VERSION = 4.3.2;
MARKETING_VERSION = 4.4.0;
OTHER_CFLAGS = (
"-DBCTBX_LOG_DOMAIN=\\\"ios\\\"",
"-DCHECK_VERSION_UPDATE=FALSE",
"-DENABLE_QRCODE=TRUE",
"-DENABLE_SMS_INVITE=TRUE",
"$(inherited)",
"-DLINPHONE_SDK_VERSION=\\\"4.4.3\\\"",
"-DLINPHONE_SDK_VERSION=\\\"4.5.0-beta.31+1a68848\\\"",
);
OTHER_SWIFT_FLAGS = "$(inherited)";
PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone;
@ -5123,7 +5133,7 @@
CODE_SIGN_STYLE = Automatic;
COMPRESS_PNG_FILES = NO;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 19;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = Z2V957B3D6;
ENABLE_BITCODE = NO;
@ -5149,14 +5159,14 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)";
LINK_WITH_STANDARD_LIBRARIES = YES;
MARKETING_VERSION = 4.3.2;
MARKETING_VERSION = 4.4.0;
OTHER_CFLAGS = (
"-DBCTBX_LOG_DOMAIN=\\\"ios\\\"",
"-DCHECK_VERSION_UPDATE=FALSE",
"-DENABLE_QRCODE=TRUE",
"-DENABLE_SMS_INVITE=TRUE",
"$(inherited)",
"-DLINPHONE_SDK_VERSION=\\\"4.4.3\\\"",
"-DLINPHONE_SDK_VERSION=\\\"4.5.0-beta.31+1a68848\\\"",
);
OTHER_SWIFT_FLAGS = "$(inherited)";
PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone;
@ -5244,7 +5254,7 @@
CODE_SIGN_STYLE = Automatic;
COMPRESS_PNG_FILES = NO;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 19;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = Z2V957B3D6;
ENABLE_BITCODE = NO;
@ -5270,14 +5280,14 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)";
LINK_WITH_STANDARD_LIBRARIES = YES;
MARKETING_VERSION = 4.3.2;
MARKETING_VERSION = 4.4.0;
OTHER_CFLAGS = (
"-DBCTBX_LOG_DOMAIN=\\\"ios\\\"",
"-DCHECK_VERSION_UPDATE=FALSE",
"-DENABLE_QRCODE=TRUE",
"-DENABLE_SMS_INVITE=TRUE",
"$(inherited)",
"-DLINPHONE_SDK_VERSION=\\\"4.4.3\\\"",
"-DLINPHONE_SDK_VERSION=\\\"4.5.0-beta.31+1a68848\\\"",
);
OTHER_SWIFT_FLAGS = "$(inherited)";
PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone;
@ -5317,7 +5327,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 19;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = Z2V957B3D6;
ENABLE_BITCODE = NO;
@ -5329,7 +5339,7 @@
GCC_WARN_UNUSED_FUNCTION = YES;
INFOPLIST_FILE = linphoneExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MARKETING_VERSION = 4.3.2;
MARKETING_VERSION = 4.4.0;
MTL_ENABLE_DEBUG_INFO = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone.linphoneExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
@ -5359,7 +5369,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 19;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = Z2V957B3D6;
ENABLE_BITCODE = NO;
@ -5370,7 +5380,7 @@
GCC_WARN_UNUSED_FUNCTION = YES;
INFOPLIST_FILE = linphoneExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MARKETING_VERSION = 4.3.2;
MARKETING_VERSION = 4.4.0;
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone.linphoneExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
@ -5400,7 +5410,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 19;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = Z2V957B3D6;
ENABLE_BITCODE = NO;
@ -5411,7 +5421,7 @@
GCC_WARN_UNUSED_FUNCTION = YES;
INFOPLIST_FILE = linphoneExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MARKETING_VERSION = 4.3.2;
MARKETING_VERSION = 4.4.0;
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone.linphoneExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
@ -5442,7 +5452,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 19;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = Z2V957B3D6;
ENABLE_BITCODE = NO;
@ -5453,7 +5463,7 @@
GCC_WARN_UNUSED_FUNCTION = YES;
INFOPLIST_FILE = linphoneExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MARKETING_VERSION = 4.3.2;
MARKETING_VERSION = 4.4.0;
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone.linphoneExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
@ -5536,7 +5546,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 19;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = Z2V957B3D6;
ENABLE_BITCODE = NO;
@ -5553,7 +5563,7 @@
INFOPLIST_FILE = "$(SRCROOT)/msgNotificationService/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 4.3.2;
MARKETING_VERSION = 4.4.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited)";
@ -5592,7 +5602,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 19;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = Z2V957B3D6;
ENABLE_BITCODE = NO;
@ -5605,7 +5615,7 @@
INFOPLIST_FILE = "$(SRCROOT)/msgNotificationService/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 4.3.2;
MARKETING_VERSION = 4.4.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited)";
@ -5644,7 +5654,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 19;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = Z2V957B3D6;
ENABLE_BITCODE = NO;
@ -5657,7 +5667,7 @@
INFOPLIST_FILE = "$(SRCROOT)/msgNotificationService/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 4.3.2;
MARKETING_VERSION = 4.4.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited)";
@ -5696,7 +5706,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 19;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = Z2V957B3D6;
ENABLE_BITCODE = NO;
@ -5709,7 +5719,7 @@
INFOPLIST_FILE = "$(SRCROOT)/msgNotificationService/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 4.3.2;
MARKETING_VERSION = 4.4.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited)";
@ -5747,7 +5757,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 19;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = Z2V957B3D6;
ENABLE_BITCODE = NO;
@ -5764,7 +5774,7 @@
INFOPLIST_FILE = msgNotificationContent/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 4.3.2;
MARKETING_VERSION = 4.4.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited)";
@ -5802,7 +5812,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 19;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = Z2V957B3D6;
ENABLE_BITCODE = NO;
@ -5815,7 +5825,7 @@
INFOPLIST_FILE = msgNotificationContent/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 4.3.2;
MARKETING_VERSION = 4.4.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited)";
@ -5853,7 +5863,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 19;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = Z2V957B3D6;
ENABLE_BITCODE = NO;
@ -5866,7 +5876,7 @@
INFOPLIST_FILE = msgNotificationContent/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 4.3.2;
MARKETING_VERSION = 4.4.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited)";
@ -5904,7 +5914,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 19;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = Z2V957B3D6;
ENABLE_BITCODE = NO;
@ -5917,7 +5927,7 @@
INFOPLIST_FILE = msgNotificationContent/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 4.3.2;
MARKETING_VERSION = 4.4.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited)";

View file

@ -30,45 +30,42 @@ enum LinphoneError: Error {
class LinphoneLoggingServiceManager: LoggingServiceDelegate {
init(config: Config, log: LoggingService?, domain: String) throws {
if let log = log {
super.init()
let debugLevel = config.getInt(section: "app", key: "debugenable_preference", defaultValue: LogLevel.Debug.rawValue)
let debugEnabled = (debugLevel >= LogLevel.Debug.rawValue && debugLevel < LogLevel.Error.rawValue)
if (debugEnabled) {
Factory.Instance.logCollectionPath = Factory.Instance.getDownloadDir(context: UnsafeMutablePointer<Int8>(mutating: (APP_GROUP_ID as NSString).utf8String))
Factory.Instance.enableLogCollection(state: LogCollectionState.Enabled)
log.domain = domain
log.logLevel = LogLevel(rawValue: debugLevel)
log.addDelegate(delegate: self)
}
Factory.Instance.logCollectionPath = Factory.Instance.getDownloadDir(context: UnsafeMutablePointer<Int8>(mutating: (APP_GROUP_ID as NSString).utf8String))
Factory.Instance.enableLogCollection(state: debugEnabled ? LogCollectionState.Enabled : LogCollectionState.Disabled)
log.domain = domain
log.logLevel = debugLevel==0 ? LogLevel.Fatal : LogLevel(rawValue: debugLevel)
log.addDelegate(delegate: self)
} else {
throw LinphoneError.loggingServiceUninitialized
}
}
override func onLogMessageWritten(logService: LoggingService, domain: String, lev: LogLevel, message: String) {
let level: String
func onLogMessageWritten(logService: LoggingService, domain: String, level: LogLevel, message: String) {
let levelStr: String
switch lev {
switch level {
case .Debug:
level = "Debug"
levelStr = "Debug"
case .Trace:
level = "Trace"
levelStr = "Trace"
case .Message:
level = "Message"
levelStr = "Message"
case .Warning:
level = "Warning"
levelStr = "Warning"
case .Error:
level = "Error"
levelStr = "Error"
case .Fatal:
level = "Fatal"
levelStr = "Fatal"
default:
level = "unknown"
levelStr = "unknown"
}
#if USE_CRASHLYTICS
Crashlytics.crashlytics().log("\(level) [\(domain)] \(message)\n")
Crashlytics.crashlytics().log("\(levelStr) [\(domain)] \(message)\n")
#endif
NSLog("\(level) [\(domain)] \(message)\n")
NSLog("\(levelStr) [\(domain)] \(message)\n")
}
}

View file

@ -31,13 +31,11 @@ var needToStop: Bool = false
var coreStopped: Bool = false
var log: LoggingService!
class NotificationViewController: UIViewController, UNNotificationContentExtension {
class NotificationViewController: UIViewController, UNNotificationContentExtension, ChatMessageDelegate, CoreDelegate {
var lc: Core?
var config: Config!
var logDelegate: LinphoneLoggingServiceManager!
var msgDelegate: LinphoneChatMessageManager!
var coreDelegate: LinphoneCoreManager!
override func viewDidLoad() {
super.viewDidLoad()
@ -82,7 +80,7 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi
}
if (needToStop) {
log.error(msg: "core stopped by app")
log.error(message: "core stopped by app")
throw LinphoneError.timeout
} else {
completion(.dismiss)
@ -90,7 +88,7 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi
}
} catch {
log.error(msg: "error: \(error)")
log.error(message: "error: \(error)")
completion(.dismissAndForwardAction)
}
}
@ -120,65 +118,59 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi
let local = try lc!.createAddress(address: localAddress)
let room = lc!.findChatRoom(peerAddr: peer, localAddr: local)
if let room = room {
msgDelegate = LinphoneChatMessageManager()
let chatMsg = try room.createMessage(message: replyText)
chatMsg.addDelegate(delegate: msgDelegate)
room.sendChatMessage(msg: chatMsg)
chatMsg.addDelegate(delegate: self)
chatMsg.send()
room.markAsRead()
}
for i in 0...50 where !isReplySent && !needToStop {
log.debug(msg: "reply \(i)")
log.debug(message: "reply \(i)")
lc!.iterate()
usleep(10000)
}
}
func startCore() throws {
config = Config.newForSharedCore(appGroupId: APP_GROUP_ID, configFilename: "linphonerc", factoryPath: "")
config = Config.newForSharedCore(appGroupId: APP_GROUP_ID, configFilename: "linphonerc", factoryConfigFilename: "")
log = LoggingService.Instance /*enable liblinphone logs.*/
logDelegate = try! LinphoneLoggingServiceManager(config: config, log: log, domain: "msgNotificationContent")
lc = try! Factory.Instance.createSharedCoreWithConfig(config: config, systemContext: nil, appGroupId: APP_GROUP_ID, mainCore: false)
coreDelegate = LinphoneCoreManager()
lc!.addDelegate(delegate: coreDelegate)
lc!.addDelegate(delegate: self)
try lc!.start()
log.message(msg: "core started")
log.message(message: "core started")
if (needToStop) {
log.error(msg: "core stopped by app")
log.error(message: "core stopped by app")
throw LinphoneError.timeout
}
}
func stopCore() {
lc!.stopAsync()
log.message(msg: "stop core")
log.message(message: "stop core")
for i in 0...100 where !coreStopped {
log.debug(msg: "stop \(i)")
log.debug(message: "stop \(i)")
lc!.iterate()
usleep(50000)
}
}
class LinphoneCoreManager: CoreDelegate {
override func onGlobalStateChanged(lc: Core, gstate: GlobalState, message: String) {
log.message(msg: "global state changed: \(gstate) : \(message) \n")
if (gstate == .Shutdown) {
needToStop = true
} else if (gstate == .Off) {
coreStopped = true
}
}
}
func onGlobalStateChanged(core: Core, state gstate: GlobalState, message: String) {
log.message(message: "global state changed: \(gstate) : \(message) \n")
if (gstate == .Shutdown) {
needToStop = true
} else if (gstate == .Off) {
coreStopped = true
}
}
class LinphoneChatMessageManager: ChatMessageDelegate {
override func onMsgStateChanged(msg: ChatMessage, state: ChatMessage.State) {
log.message(msg: "msg state changed: \(state)\n")
if (state == .Delivered) {
isReplySent = true
}
}
}
func onMsgStateChanged(message: ChatMessage, state: ChatMessage.State) {
log.message(message: "msg state changed: \(state)\n")
if (state == .Delivered) {
isReplySent = true
}
}
}

View file

@ -58,15 +58,15 @@ class NotificationService: UNNotificationServiceExtension {
if let bestAttemptContent = bestAttemptContent {
createCore()
NotificationService.log.message(msg: "received push payload : \(bestAttemptContent.userInfo.debugDescription)")
NotificationService.log.message(message: "received push payload : \(bestAttemptContent.userInfo.debugDescription)")
if let chatRoomInviteAddr = bestAttemptContent.userInfo["chat-room-addr"] as? String, !chatRoomInviteAddr.isEmpty {
NotificationService.log.message(msg: "fetch chat room for invite, addr: \(chatRoomInviteAddr)")
NotificationService.log.message(message: "fetch chat room for invite, addr: \(chatRoomInviteAddr)")
let chatRoom = lc!.getNewChatRoomFromConfAddr(chatRoomAddr: chatRoomInviteAddr)
if let chatRoom = chatRoom {
stopCore()
NotificationService.log.message(msg: "chat room invite received")
NotificationService.log.message(message: "chat room invite received")
bestAttemptContent.title = NSLocalizedString("GC_MSG", comment: "")
if (chatRoom.hasCapability(mask:ChatRoomCapabilities.OneToOne.rawValue)) {
if (chatRoom.peerAddress?.displayName.isEmpty != true) {
@ -83,13 +83,15 @@ class NotificationService: UNNotificationServiceExtension {
return
}
} else if let callId = bestAttemptContent.userInfo["call-id"] as? String {
NotificationService.log.message(msg: "fetch msg for callid ["+callId+"]")
NotificationService.log.message(message: "fetch msg for callid ["+callId+"]")
let message = lc!.getNewMessageFromCallid(callId: callId)
if let message = message {
let msgData = parseMessage(message: message)
if !message.isUsingUserDefaults, let badge = updateBadge() as NSNumber? {
// Extension only upates app's badge when main shared core is Off = extension's core is On.
// Otherwise, the app will update the badge.
if lc?.globalState == GlobalState.On, let badge = updateBadge() as NSNumber? {
bestAttemptContent.badge = badge
}
@ -114,7 +116,7 @@ class NotificationService: UNNotificationServiceExtension {
contentHandler(bestAttemptContent)
return
} else {
NotificationService.log.message(msg: "Message not found for callid ["+callId+"]")
NotificationService.log.message(message: "Message not found for callid ["+callId+"]")
}
}
serviceExtensionTimeWillExpire()
@ -124,7 +126,7 @@ class NotificationService: UNNotificationServiceExtension {
override func serviceExtensionTimeWillExpire() {
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
NotificationService.log.warning(msg: "serviceExtensionTimeWillExpire")
NotificationService.log.warning(message: "serviceExtensionTimeWillExpire")
stopCore()
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
NSLog("[msgNotificationService] serviceExtensionTimeWillExpire")
@ -174,15 +176,15 @@ class NotificationService: UNNotificationServiceExtension {
}
}
NotificationService.log.message(msg: "msg: \(content) \n")
NotificationService.log.message(message: "msg: \(content) \n")
return msgData;
}
func createCore() {
NSLog("[msgNotificationService] create core")
let config = Config.newForSharedCore(appGroupId: APP_GROUP_ID, configFilename: "linphonerc", factoryPath: "")
let config = Config.newForSharedCore(appGroupId: APP_GROUP_ID, configFilename: "linphonerc", factoryConfigFilename: "")
if (NotificationService.log == nil || NotificationService.log.getDelegate() == nil) {
if (NotificationService.log == nil) {
NotificationService.log = LoggingService.Instance /*enable liblinphone logs.*/
NotificationService.logDelegate = try! LinphoneLoggingServiceManager(config: config!, log: NotificationService.log, domain: "msgNotificationService")
}
@ -190,7 +192,7 @@ class NotificationService: UNNotificationServiceExtension {
}
func stopCore() {
NotificationService.log.message(msg: "stop core")
NotificationService.log.message(message: "stop core")
if let lc = lc {
lc.stop()
}
@ -201,14 +203,14 @@ class NotificationService: UNNotificationServiceExtension {
count += lc!.unreadChatMessageCount
count += lc!.missedCallsCount
count += lc!.callsNb
NotificationService.log.message(msg: "badge: \(count)\n")
NotificationService.log.message(message: "badge: \(count)\n")
return count
}
func getDisplayNameFromSipAddress(sipAddr: String?) -> String? {
if let sipAddr = sipAddr {
NotificationService.log.message(msg: "looking for display name for \(sipAddr)")
NotificationService.log.message(message: "looking for display name for \(sipAddr)")
if (sipAddr == "") { return nil }
@ -216,17 +218,21 @@ class NotificationService: UNNotificationServiceExtension {
let addressBook = defaults?.dictionary(forKey: "addressBook")
if (addressBook == nil) {
NotificationService.log.message(msg: "address book not found in userDefaults")
NotificationService.log.message(message: "address book not found in userDefaults")
return nil
}
if let displayName = addressBook?[sipAddr] as? String {
NotificationService.log.message(msg: "display name for \(sipAddr): \(displayName)")
return displayName
} else {
NotificationService.log.message(msg: "display name for \(sipAddr) not found in userDefaults")
return nil
if let simpleAddr = lc?.interpretUrl(url: sipAddr) {
simpleAddr.clean()
let nomalSipaddr = simpleAddr.asString()
if let displayName = addressBook?[nomalSipaddr] as? String {
NotificationService.log.message(message: "display name for \(sipAddr): \(displayName)")
return displayName
}
}
NotificationService.log.message(message: "display name for \(sipAddr) not found in userDefaults")
return nil
}
return nil
}