forked from mirrors/linphone-iphone
Merge new UI for Call and Conferences
This commit is contained in:
commit
524d429960
265 changed files with 11977 additions and 7957 deletions
|
|
@ -7,13 +7,13 @@ variables:
|
|||
job-ios:
|
||||
|
||||
stage: build
|
||||
tags: [ "macosx-xcode12" ]
|
||||
tags: [ "macmini-m1-xcode13" ]
|
||||
|
||||
script:
|
||||
- pod install --repo-update
|
||||
- pwd
|
||||
- xcodebuild archive -scheme $archive_scheme -archivePath ./$archive_path -configuration Release -workspace ./linphone.xcworkspace -UseModernBuildSystem=NO
|
||||
- xcodebuild -exportArchive -archivePath ./$archive_path -exportPath ./$export_path -exportOptionsPlist ./$export_options_plist -allowProvisioningUpdates -UseModernBuildSystem=NO
|
||||
- xcodebuild archive -scheme $archive_scheme -archivePath ./$archive_path -configuration Release -workspace ./linphone.xcworkspace -UseModernBuildSystem=YES -destination 'generic/platform=iOS'
|
||||
- xcodebuild -exportArchive -archivePath ./$archive_path -exportPath ./$export_path -exportOptionsPlist ./$export_options_plist -allowProvisioningUpdates -UseModernBuildSystem=YES -destination 'generic/platform=iOS'
|
||||
|
||||
|
||||
artifacts:
|
||||
|
|
|
|||
|
|
@ -1,52 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#import "AudioHelper.h"
|
||||
|
||||
@implementation AudioHelper
|
||||
|
||||
+ (NSArray *)bluetoothRoutes {
|
||||
return @[AVAudioSessionPortBluetoothHFP, AVAudioSessionPortCarAudio, AVAudioSessionPortBluetoothA2DP, AVAudioSessionPortBluetoothLE ];
|
||||
}
|
||||
|
||||
+ (AVAudioSessionPortDescription *)bluetoothAudioDevice {
|
||||
return [AudioHelper audioDeviceFromTypes:[AudioHelper bluetoothRoutes]];
|
||||
}
|
||||
|
||||
+ (AVAudioSessionPortDescription *)builtinAudioDevice {
|
||||
NSArray *builtinRoutes = @[ AVAudioSessionPortBuiltInMic ];
|
||||
return [AudioHelper audioDeviceFromTypes:builtinRoutes];
|
||||
}
|
||||
|
||||
+ (AVAudioSessionPortDescription *)speakerAudioDevice {
|
||||
NSArray *builtinRoutes = @[ AVAudioSessionPortBuiltInSpeaker ];
|
||||
return [AudioHelper audioDeviceFromTypes:builtinRoutes];
|
||||
}
|
||||
|
||||
+ (AVAudioSessionPortDescription *)audioDeviceFromTypes:(NSArray *)types {
|
||||
NSArray *routes = [[AVAudioSession sharedInstance] availableInputs];
|
||||
for (AVAudioSessionPortDescription *route in routes) {
|
||||
if ([types containsObject:route.portType]) {
|
||||
return route;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -1,317 +0,0 @@
|
|||
<?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" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="landscape" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="CallIncomingView">
|
||||
<connections>
|
||||
<outlet property="addressLabel" destination="78f-eb-xdx" id="Qjw-7G-oqG"/>
|
||||
<outlet property="avatarImage" destination="19" id="20"/>
|
||||
<outlet property="earlyMediaView" destination="lUj-2n-Afx" id="cHv-3Y-lIQ"/>
|
||||
<outlet property="landscapeView" destination="r7T-Et-xrQ" id="rA1-2K-fUf"/>
|
||||
<outlet property="nameLabel" destination="hjQ-4P-bKP" id="Elh-o8-zM9"/>
|
||||
<outlet property="portraitView" destination="25" id="6Gy-ZX-kTl"/>
|
||||
<outlet property="tabBar" destination="4" id="9gd-FT-jaI"/>
|
||||
<outlet property="tabVideoBar" destination="vIQ-QP-ooa" id="JZq-9l-pOy"/>
|
||||
<outlet property="view" destination="25" id="26"/>
|
||||
</connections>
|
||||
</placeholder>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="6e0-76-KvA" userLabel="iphone6MetricsView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="667" height="375"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<view tag="1" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="25">
|
||||
<rect key="frame" x="0.0" y="42" width="667" height="333"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" tag="3" contentMode="scaleAspectFit" fixedFrame="YES" image="avatar.png" translatesAutoresizingMaskIntoConstraints="NO" id="19" userLabel="avatarImage" customClass="UIRoundedImageView">
|
||||
<rect key="frame" x="155" y="147" width="355" height="107"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Contact avatar">
|
||||
<accessibilityTraits key="traits" image="YES" notEnabled="YES"/>
|
||||
<bool key="isElement" value="YES"/>
|
||||
</accessibility>
|
||||
</imageView>
|
||||
<button opaque="NO" tag="2" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Cro-ww-VIC" userLabel="headerBar">
|
||||
<rect key="frame" x="0.0" y="0.0" width="667" height="66"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration">
|
||||
<accessibilityTraits key="traits" button="YES" notEnabled="YES"/>
|
||||
</accessibility>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="27"/>
|
||||
<state key="normal" title="INCOMING CALL" backgroundImage="color_F.png">
|
||||
<color key="titleColor" red="0.98766469955444336" green="0.27512490749359131" blue="0.029739789664745331" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
</button>
|
||||
<view tag="6" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="4" userLabel="tabBar">
|
||||
<rect key="frame" x="0.0" y="270" width="667" height="63"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<button opaque="NO" tag="7" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6" userLabel="declineButton">
|
||||
<rect key="frame" x="0.0" y="0.0" width="334" height="63"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Decline"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
|
||||
<state key="normal" image="call_hangup_default.png" backgroundImage="color_D.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="disabled" image="call_hangup_disabled.png"/>
|
||||
<state key="highlighted" backgroundImage="color_I.png"/>
|
||||
<connections>
|
||||
<action selector="onDeclineClick:" destination="-1" eventType="touchUpInside" id="16"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" tag="8" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="7" userLabel="acceptButton">
|
||||
<rect key="frame" x="333" y="0.0" width="334" height="63"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" heightSizable="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Accept"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
|
||||
<state key="normal" image="call_audio_start_default.png" backgroundImage="color_A.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="disabled" image="call_audio_start_disabled.png"/>
|
||||
<state key="highlighted" backgroundImage="color_L.png"/>
|
||||
<connections>
|
||||
<action selector="onAcceptClick:" destination="-1" eventType="touchUpInside" id="15"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.98766469955444336" green="0.27512490749359131" blue="0.029739789664745331" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</view>
|
||||
<view hidden="YES" tag="9" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="vIQ-QP-ooa" userLabel="tabVideoBar">
|
||||
<rect key="frame" x="0.0" y="270" width="667" height="63"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<button opaque="NO" tag="10" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="KnH-hj-g47" userLabel="declineButton">
|
||||
<rect key="frame" x="0.0" y="0.0" width="222" height="63"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Decline"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
|
||||
<state key="normal" image="call_hangup_default.png" backgroundImage="color_D.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="disabled" image="call_hangup_disabled.png"/>
|
||||
<state key="highlighted" backgroundImage="color_I.png"/>
|
||||
<connections>
|
||||
<action selector="onDeclineClick:" destination="-1" eventType="touchUpInside" id="Nrs-UR-Hb9"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" tag="11" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="wYo-ty-Rwk" userLabel="acceptAudioOnlyButton">
|
||||
<rect key="frame" x="222" y="0.0" width="223" height="63"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Accept"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
|
||||
<state key="normal" image="call_audio_start_default.png" backgroundImage="color_A.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="disabled" image="call_audio_start_disabled.png"/>
|
||||
<state key="highlighted" backgroundImage="color_L.png"/>
|
||||
<connections>
|
||||
<action selector="onAcceptAudioOnlyClick:" destination="-1" eventType="touchUpInside" id="N9h-i1-ejZ"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" tag="12" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="tX0-eE-di5" userLabel="acceptButton">
|
||||
<rect key="frame" x="445" y="0.0" width="222" height="63"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Accept"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
|
||||
<state key="normal" image="call_video_start_default.png" backgroundImage="color_A.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="disabled" image="call_video_start_disabled.png"/>
|
||||
<state key="selected" image="call_video_start_disabled.png"/>
|
||||
<state key="highlighted" backgroundImage="color_L.png"/>
|
||||
<connections>
|
||||
<action selector="onAcceptClick:" destination="-1" eventType="touchUpInside" id="XvK-9T-J2j"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.98766469955444336" green="0.27512490749359131" blue="0.029739789664745331" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</view>
|
||||
<view hidden="YES" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="lUj-2n-Afx" userLabel="earlyMediaView">
|
||||
<rect key="frame" x="0.0" y="35" width="667" height="265"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" tag="5" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="john.doe@sip.linphone.org" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="8" translatesAutoresizingMaskIntoConstraints="NO" id="78f-eb-xdx" userLabel="addressLabel">
|
||||
<rect key="frame" x="0.0" y="111" width="667" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="18"/>
|
||||
<color key="textColor" red="0.98766469955444336" green="0.27512490749359131" blue="0.029739789664745331" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" tag="4" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="John Doe" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="19" translatesAutoresizingMaskIntoConstraints="NO" id="hjQ-4P-bKP" userLabel="nameLabel">
|
||||
<rect key="frame" x="0.0" y="79" width="667" height="28"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="33"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<point key="canvasLocation" x="-141.59999999999999" y="42.728635682158924"/>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" id="Znq-C0-ZAc" userLabel="iphone6MetricsView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="667" height="375"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<view tag="1" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="r7T-Et-xrQ">
|
||||
<rect key="frame" x="0.0" y="42" width="667" height="333"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<button opaque="NO" tag="2" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="E9b-qt-GBq" userLabel="headerBar">
|
||||
<rect key="frame" x="0.0" y="0.0" width="667" height="66"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration">
|
||||
<accessibilityTraits key="traits" button="YES" notEnabled="YES"/>
|
||||
</accessibility>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="27"/>
|
||||
<state key="normal" title="INCOMING CALL" backgroundImage="color_F.png">
|
||||
<color key="titleColor" red="0.98766469955444336" green="0.27512490749359131" blue="0.029739789664745331" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
</button>
|
||||
<view tag="6" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="0Tc-7G-eqT" userLabel="tabBar">
|
||||
<rect key="frame" x="0.0" y="270" width="667" height="63"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<button opaque="NO" tag="7" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="qpN-iY-3Ao" userLabel="declineButton">
|
||||
<rect key="frame" x="0.0" y="0.0" width="334" height="63"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Decline"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
|
||||
<state key="normal" image="call_hangup_default.png" backgroundImage="color_D.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="disabled" image="call_hangup_disabled.png"/>
|
||||
<state key="highlighted" backgroundImage="color_I.png"/>
|
||||
<connections>
|
||||
<action selector="onDeclineClick:" destination="-1" eventType="touchUpInside" id="JKz-8y-c9T"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" tag="8" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="8dX-3c-mba" userLabel="acceptButton">
|
||||
<rect key="frame" x="333" y="0.0" width="334" height="63"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" heightSizable="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Accept"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
|
||||
<state key="normal" image="call_audio_start_default.png" backgroundImage="color_A.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="disabled" image="call_audio_start_disabled.png"/>
|
||||
<state key="highlighted" backgroundImage="color_L.png"/>
|
||||
<connections>
|
||||
<action selector="onAcceptClick:" destination="-1" eventType="touchUpInside" id="17v-kE-yOu"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.98766469955444336" green="0.27512490749359131" blue="0.029739789664745331" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</view>
|
||||
<view hidden="YES" tag="9" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="PPE-Fd-wDf" userLabel="tabVideoBar">
|
||||
<rect key="frame" x="0.0" y="270" width="667" height="63"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<button opaque="NO" tag="10" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="i8b-dr-IYG" userLabel="declineButton">
|
||||
<rect key="frame" x="0.0" y="0.0" width="221" height="63"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Decline"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
|
||||
<state key="normal" image="call_hangup_default.png" backgroundImage="color_D.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="disabled" image="call_hangup_disabled.png"/>
|
||||
<state key="highlighted" backgroundImage="color_I.png"/>
|
||||
<connections>
|
||||
<action selector="onDeclineClick:" destination="-1" eventType="touchUpInside" id="mjN-BB-4ph"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" tag="11" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="QYg-9G-We0" userLabel="acceptAudioOnlyButton">
|
||||
<rect key="frame" x="221" y="0.0" width="224" height="63"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Accept"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
|
||||
<state key="normal" image="call_audio_start_default.png" backgroundImage="color_A.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="disabled" image="call_audio_start_disabled.png"/>
|
||||
<state key="highlighted" backgroundImage="color_L.png"/>
|
||||
<connections>
|
||||
<action selector="onAcceptAudioOnlyClick:" destination="-1" eventType="touchUpInside" id="veh-c0-GOe"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" tag="12" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="sbd-NW-OZx" userLabel="acceptButton">
|
||||
<rect key="frame" x="445" y="0.0" width="222" height="63"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Accept"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
|
||||
<state key="normal" image="call_video_start_default.png" backgroundImage="color_A.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="disabled" image="call_video_start_disabled.png"/>
|
||||
<state key="selected" image="call_video_start_disabled.png"/>
|
||||
<state key="highlighted" backgroundImage="color_L.png"/>
|
||||
<connections>
|
||||
<action selector="onAcceptClick:" destination="-1" eventType="touchUpInside" id="RLl-y6-yhs"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.98766469955444336" green="0.27512490749359131" blue="0.029739789664745331" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</view>
|
||||
<imageView userInteractionEnabled="NO" tag="3" contentMode="scaleAspectFit" fixedFrame="YES" image="avatar.png" translatesAutoresizingMaskIntoConstraints="NO" id="Q0C-CO-AYR" userLabel="avatarImage" customClass="UIRoundedImageView">
|
||||
<rect key="frame" x="110" y="74" width="180" height="180"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Contact avatar">
|
||||
<accessibilityTraits key="traits" image="YES" notEnabled="YES"/>
|
||||
<bool key="isElement" value="YES"/>
|
||||
</accessibility>
|
||||
</imageView>
|
||||
<view hidden="YES" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="F11-a0-ZL6" userLabel="earlyMediaView">
|
||||
<rect key="frame" x="45" y="73" width="250" height="187"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" tag="5" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="john.doe@sip.linphone.org" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="8" translatesAutoresizingMaskIntoConstraints="NO" id="tsb-6p-cAk" userLabel="addressLabel">
|
||||
<rect key="frame" x="297" y="166" width="370" height="33"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="18"/>
|
||||
<color key="textColor" red="0.98766469955444336" green="0.27512490749359131" blue="0.029739789664745331" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" tag="4" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="John Doe" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="19" translatesAutoresizingMaskIntoConstraints="NO" id="WsB-At-ejv" userLabel="nameLabel">
|
||||
<rect key="frame" x="297" y="121" width="370" height="37"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="33"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<point key="canvasLocation" x="762.39999999999998" y="-88.605697151424295"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="avatar.png" width="414.39999389648438" height="414.39999389648438"/>
|
||||
<image name="call_audio_start_default.png" width="58.400001525878906" height="58.400001525878906"/>
|
||||
<image name="call_audio_start_disabled.png" width="58.400001525878906" height="58.400001525878906"/>
|
||||
<image name="call_hangup_default.png" width="67.199996948242188" height="58.400001525878906"/>
|
||||
<image name="call_hangup_disabled.png" width="67.199996948242188" height="58.400001525878906"/>
|
||||
<image name="call_video_start_default.png" width="64" height="58.400001525878906"/>
|
||||
<image name="call_video_start_disabled.png" width="64" height="58.400001525878906"/>
|
||||
<image name="color_A.png" width="2" height="2"/>
|
||||
<image name="color_D.png" width="2" height="2"/>
|
||||
<image name="color_F.png" width="2" height="2"/>
|
||||
<image name="color_I.png" width="2" height="2"/>
|
||||
<image name="color_L.png" width="2" height="2"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
@ -1,623 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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="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="CallOutgoingView">
|
||||
<connections>
|
||||
<outlet property="addressLabel" destination="2fa-Ag-3GW" id="2Oe-UW-rPC"/>
|
||||
<outlet property="avatarImage" destination="bNo-O5-DWh" id="eqo-0s-UoN"/>
|
||||
<outlet property="declineButton" destination="AaM-cH-pvW" id="nET-cl-Kfb"/>
|
||||
<outlet property="declineButton_earlyMedia" destination="4AS-bf-34N" id="SQm-um-bgq"/>
|
||||
<outlet property="eightButton" destination="t0H-I1-ukQ" id="vOh-0j-erj"/>
|
||||
<outlet property="fiveButton" destination="iU3-pL-jRO" id="1wx-4h-Gvl"/>
|
||||
<outlet property="fourButton" destination="Xfq-Ej-VHd" id="aAF-q4-Thm"/>
|
||||
<outlet property="hashButton" destination="yiW-6Q-8Gn" id="SvV-4h-CQq"/>
|
||||
<outlet property="landscapeView" destination="Czn-ec-dh8" id="ZIk-2g-9Qk"/>
|
||||
<outlet property="microButton" destination="26e-Pj-2Oh" id="jXh-0n-yg7"/>
|
||||
<outlet property="nameLabel" destination="d5s-yP-8VE" id="0VY-HP-ovD"/>
|
||||
<outlet property="nineButton" destination="H98-w7-FtJ" id="Y29-sY-bs7"/>
|
||||
<outlet property="numpadButton" destination="1Ya-Ok-b84" id="R3l-8u-Bcl"/>
|
||||
<outlet property="numpadView" destination="5UV-gd-B5P" id="bOc-Em-4g1"/>
|
||||
<outlet property="oneButton" destination="YNy-y3-Vfb" id="mib-jj-IWo"/>
|
||||
<outlet property="portraitView" destination="25" id="26I-da-00C"/>
|
||||
<outlet property="routesBluetoothButton" destination="SH1-xD-Agw" id="ifX-Dy-Tcb"/>
|
||||
<outlet property="routesButton" destination="29K-Sd-aHF" id="MVM-Mb-OWE"/>
|
||||
<outlet property="routesEarpieceButton" destination="zs4-Zy-FrP" id="TOt-D3-635"/>
|
||||
<outlet property="routesSpeakerButton" destination="oKz-6p-EAF" id="yIx-qF-Gd7"/>
|
||||
<outlet property="routesView" destination="iyf-nk-ORJ" id="LBJ-Rm-VUX"/>
|
||||
<outlet property="sevenButton" destination="yAW-CE-SCK" id="Zhm-Oo-U7v"/>
|
||||
<outlet property="sixButton" destination="c3c-wj-OO9" id="Dmk-J0-8Mc"/>
|
||||
<outlet property="speakerButton" destination="G7m-Av-QlR" id="UHW-L2-NDM"/>
|
||||
<outlet property="starButton" destination="MdJ-SK-bIH" id="Ri3-EC-1oq"/>
|
||||
<outlet property="threeButton" destination="gXh-2W-waU" id="Neg-lD-uwc"/>
|
||||
<outlet property="twoButton" destination="wrx-1J-g34" id="e17-3F-hcV"/>
|
||||
<outlet property="view" destination="25" id="26"/>
|
||||
<outlet property="zeroButton" destination="49E-lz-Dx1" id="Car-8R-zs1"/>
|
||||
</connections>
|
||||
</placeholder>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="HpM-if-114" userLabel="iphone6MetricsView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<view tag="1" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="25">
|
||||
<rect key="frame" x="0.0" y="42" width="414" height="854"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<button opaque="NO" tag="2" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="NFl-sb-0TV" userLabel="headerBar">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="66"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration">
|
||||
<accessibilityTraits key="traits" button="YES" notEnabled="YES"/>
|
||||
</accessibility>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="27"/>
|
||||
<state key="normal" title="OUTGOING CALL" backgroundImage="color_F.png">
|
||||
<color key="titleColor" red="0.98766469955444336" green="0.27512490749359131" blue="0.029739789664745331" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
</button>
|
||||
<view hidden="YES" tag="37" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="iyf-nk-ORJ" userLabel="routesView">
|
||||
<rect key="frame" x="311" y="593" width="104" height="198"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" tag="38" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="color_F.png" translatesAutoresizingMaskIntoConstraints="NO" id="xEp-Iw-uII" userLabel="backgroundColor">
|
||||
<rect key="frame" x="0.0" y="0.0" width="104" height="198"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
</imageView>
|
||||
<button opaque="NO" tag="39" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" adjustsImageWhenDisabled="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="SH1-xD-Agw" userLabel="routesBluetoothButton" customClass="UIBluetoothButton">
|
||||
<rect key="frame" x="0.0" y="0.0" width="104" height="66"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Bluetooth"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="13"/>
|
||||
<inset key="titleEdgeInsets" minX="0.0" minY="28" maxX="0.0" maxY="0.0"/>
|
||||
<state key="normal" image="route_bluetooth_default.png">
|
||||
<color key="titleColor" red="0.19582480192184448" green="0.21824681758880615" blue="0.24259182810783386" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="disabled" image="route_bluetooth_disabled.png">
|
||||
<color key="titleColor" red="0.58667385578155518" green="0.64151597023010254" blue="0.69586050510406494" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="selected" image="route_bluetooth_selected.png"/>
|
||||
<state key="highlighted" backgroundImage="color_E.png">
|
||||
<color key="titleColor" red="0.76000285148620605" green="0.21515019237995148" blue="0.12376989424228668" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="onRoutesBluetoothClick:" destination="-1" eventType="touchUpInside" id="0bq-3n-xii"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" tag="40" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" adjustsImageWhenDisabled="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="zs4-Zy-FrP" userLabel="routesEarpieceButton">
|
||||
<rect key="frame" x="0.0" y="66" width="104" height="66"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Receiver"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="13"/>
|
||||
<inset key="titleEdgeInsets" minX="0.0" minY="28" maxX="0.0" maxY="0.0"/>
|
||||
<state key="normal" image="route_earpiece_default.png">
|
||||
<color key="titleColor" red="0.19582480192184448" green="0.21824681758880615" blue="0.24259182810783386" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="disabled" image="route_earpiece_disabled.png">
|
||||
<color key="titleColor" red="0.58667385578155518" green="0.64151597023010254" blue="0.69586050510406494" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="selected" image="route_earpiece_selected.png"/>
|
||||
<state key="highlighted" backgroundImage="color_E.png">
|
||||
<color key="titleColor" red="0.76000285148620605" green="0.21515019237995148" blue="0.12376989424228668" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="onRoutesEarpieceClick:" destination="-1" eventType="touchUpInside" id="YRr-Kn-GgV"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" tag="41" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" adjustsImageWhenDisabled="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="oKz-6p-EAF" userLabel="routesSpeakerButton" customClass="UISpeakerButton">
|
||||
<rect key="frame" x="0.0" y="132" width="104" height="66"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Speaker"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="13"/>
|
||||
<inset key="titleEdgeInsets" minX="0.0" minY="28" maxX="0.0" maxY="0.0"/>
|
||||
<state key="normal" image="route_speaker_default.png">
|
||||
<color key="titleColor" red="0.19582480192184448" green="0.21824681758880615" blue="0.24259182810783386" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="disabled" image="route_speaker_disabled.png">
|
||||
<color key="titleColor" red="0.58667385578155518" green="0.64151597023010254" blue="0.69586050510406494" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="selected" image="route_speaker_selected.png"/>
|
||||
<state key="highlighted" backgroundImage="color_E.png">
|
||||
<color key="titleColor" red="0.76000285148620605" green="0.21515019237995148" blue="0.12376989424228668" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="onRoutesSpeakerClick:" destination="-1" eventType="touchUpInside" id="g4L-6P-Iqw"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
</view>
|
||||
<view tag="7" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="8Qi-Cq-3XH" userLabel="tabBar">
|
||||
<rect key="frame" x="0.0" y="791" width="414" height="63"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" tag="8" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="color_F.png" translatesAutoresizingMaskIntoConstraints="NO" id="vyh-Us-8kj" userLabel="backgroundColor">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="63"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||
</imageView>
|
||||
<button opaque="NO" tag="11" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="AaM-cH-pvW" userLabel="declineButton">
|
||||
<rect key="frame" x="0.0" y="0.0" width="207" height="63"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Decline"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
|
||||
<state key="normal" image="call_hangup_default.png" backgroundImage="color_D.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="disabled" image="call_hangup_disabled.png"/>
|
||||
<state key="highlighted" backgroundImage="color_I.png"/>
|
||||
<connections>
|
||||
<action selector="onDeclineClick:" destination="-1" eventType="touchUpInside" id="Ebl-hM-8F9"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" tag="3057" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="1Ya-Ok-b84" userLabel="numpadButton" customClass="UIToggleButton">
|
||||
<rect key="frame" x="0.0" y="0.0" width="94" height="63"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Back"/>
|
||||
<state key="normal" image="footer_dialer_default.png" backgroundImage="color_C.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="disabled" image="footer_dialer_disabled.png"/>
|
||||
<state key="selected" image="dialer_alt_back.png"/>
|
||||
<state key="highlighted" backgroundImage="color_A.png"/>
|
||||
<connections>
|
||||
<action selector="onNumpadClick:" destination="-1" eventType="touchUpInside" id="EeE-il-gTD"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" tag="3011" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="4AS-bf-34N" userLabel="declineButton_earlyMedia">
|
||||
<rect key="frame" x="93" y="0.0" width="114" height="63"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Decline"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
|
||||
<state key="normal" image="call_hangup_default.png" backgroundImage="color_D.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="disabled" image="call_hangup_disabled.png"/>
|
||||
<state key="highlighted" backgroundImage="color_I.png"/>
|
||||
<connections>
|
||||
<action selector="onDeclineClick:" destination="-1" eventType="touchUpInside" id="alt-Ma-tLc"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" tag="9" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="26e-Pj-2Oh" userLabel="microButton" customClass="UIMutedMicroButton">
|
||||
<rect key="frame" x="207" y="0.0" width="104" height="63"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Accept"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
|
||||
<state key="normal" image="micro_default.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="disabled" image="micro_disabled.png"/>
|
||||
<state key="selected" image="micro_selected.png"/>
|
||||
<state key="highlighted" backgroundImage="color_E.png"/>
|
||||
</button>
|
||||
<button hidden="YES" opaque="NO" tag="54" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="29K-Sd-aHF" userLabel="routesButton" customClass="UIToggleButton">
|
||||
<rect key="frame" x="311" y="0.0" width="104" height="63"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Route"/>
|
||||
<inset key="titleEdgeInsets" minX="0.0" minY="38" maxX="0.0" maxY="0.0"/>
|
||||
<state key="normal" image="routes_default.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="disabled" image="routes_disabled.png"/>
|
||||
<state key="selected" image="routes_selected.png"/>
|
||||
<state key="highlighted" backgroundImage="color_E.png"/>
|
||||
<connections>
|
||||
<action selector="onRoutesClick:" destination="-1" eventType="touchUpInside" id="hXX-8a-7M4"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" tag="10" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="G7m-Av-QlR" userLabel="speakerButton" customClass="UISpeakerButton">
|
||||
<rect key="frame" x="311" y="0.0" width="104" height="63"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Accept"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
|
||||
<state key="normal" image="speaker_default.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="disabled" image="speaker_disabled.png"/>
|
||||
<state key="selected" image="speaker_selected.png"/>
|
||||
<state key="highlighted" backgroundImage="color_E.png"/>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" tag="5" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="john.doe@sip.linphone.org" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="2fa-Ag-3GW" userLabel="addressLabel">
|
||||
<rect key="frame" x="4" y="281" width="405" height="46"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="18"/>
|
||||
<color key="textColor" red="0.98766469955444336" green="0.27512490749359131" blue="0.029739789664745331" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" tag="4" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="John Doe" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="d5s-yP-8VE" userLabel="nameLabel">
|
||||
<rect key="frame" x="0.0" y="207" width="414" height="71"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="33"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<imageView userInteractionEnabled="NO" tag="6" contentMode="scaleAspectFit" fixedFrame="YES" image="avatar.png" translatesAutoresizingMaskIntoConstraints="NO" id="bNo-O5-DWh" userLabel="avatarImage" customClass="UIRoundedImageView">
|
||||
<rect key="frame" x="96" y="374" width="220" height="273"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Contact avatar">
|
||||
<accessibilityTraits key="traits" image="YES" notEnabled="YES"/>
|
||||
<bool key="isElement" value="YES"/>
|
||||
</accessibility>
|
||||
</imageView>
|
||||
<view hidden="YES" tag="3024" contentMode="scaleToFill" id="5UV-gd-B5P" userLabel="numpadView">
|
||||
<rect key="frame" x="0.0" y="65" width="414" height="726"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<button opaque="NO" clearsContextBeforeDrawing="NO" tag="3025" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="YNy-y3-Vfb" userLabel="1" customClass="UIDigitButton">
|
||||
<rect key="frame" x="-1" y="1" width="134" height="169"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<color key="backgroundColor" red="0.40000001000000002" green="1" blue="1" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<accessibility key="accessibilityConfiguration" label="1"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<state key="normal" image="numpad_1_default.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="selected" image="numpad_1_over.png"/>
|
||||
<state key="highlighted" image="numpad_1_over.png" backgroundImage="numpad_over_background.png"/>
|
||||
</button>
|
||||
<button opaque="NO" clearsContextBeforeDrawing="NO" tag="3026" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="wrx-1J-g34" userLabel="2" customClass="UIDigitButton">
|
||||
<rect key="frame" x="138" y="1" width="134" height="169"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="2"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<state key="normal" image="numpad_2_default.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="highlighted" image="numpad_2_over.png" backgroundImage="numpad_over_background.png"/>
|
||||
</button>
|
||||
<button opaque="NO" clearsContextBeforeDrawing="NO" tag="3027" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="gXh-2W-waU" userLabel="3" customClass="UIDigitButton">
|
||||
<rect key="frame" x="277" y="1" width="134" height="169"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="3"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<state key="normal" image="numpad_3_default.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="highlighted" image="numpad_3_over.png" backgroundImage="numpad_over_background.png"/>
|
||||
</button>
|
||||
<button opaque="NO" clearsContextBeforeDrawing="NO" tag="3028" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Xfq-Ej-VHd" userLabel="4" customClass="UIDigitButton">
|
||||
<rect key="frame" x="-1" y="186" width="134" height="166"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="4"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<state key="normal" image="numpad_4_default.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="highlighted" image="numpad_4_over.png" backgroundImage="numpad_over_background.png"/>
|
||||
</button>
|
||||
<button opaque="NO" clearsContextBeforeDrawing="NO" tag="3029" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="iU3-pL-jRO" userLabel="5" customClass="UIDigitButton">
|
||||
<rect key="frame" x="138" y="186" width="134" height="166"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<state key="normal" image="numpad_5_default.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="highlighted" image="numpad_5_over.png" backgroundImage="numpad_over_background.png"/>
|
||||
</button>
|
||||
<button opaque="NO" clearsContextBeforeDrawing="NO" tag="3030" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="c3c-wj-OO9" userLabel="6" customClass="UIDigitButton">
|
||||
<rect key="frame" x="277" y="186" width="134" height="166"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="6"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<state key="normal" image="numpad_6_default.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="highlighted" image="numpad_6_over.png" backgroundImage="numpad_over_background.png"/>
|
||||
</button>
|
||||
<button opaque="NO" clearsContextBeforeDrawing="NO" tag="3031" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="yAW-CE-SCK" userLabel="7" customClass="UIDigitButton">
|
||||
<rect key="frame" x="-1" y="360" width="134" height="166"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="7"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<state key="normal" image="numpad_7_default.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="highlighted" image="numpad_7_over.png" backgroundImage="numpad_over_background.png"/>
|
||||
</button>
|
||||
<button opaque="NO" clearsContextBeforeDrawing="NO" tag="3032" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="t0H-I1-ukQ" userLabel="8" customClass="UIDigitButton">
|
||||
<rect key="frame" x="138" y="360" width="134" height="166"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="8"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<state key="normal" image="numpad_8_default.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="highlighted" image="numpad_8_over.png" backgroundImage="numpad_over_background.png"/>
|
||||
</button>
|
||||
<button opaque="NO" clearsContextBeforeDrawing="NO" tag="3033" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="H98-w7-FtJ" userLabel="9" customClass="UIDigitButton">
|
||||
<rect key="frame" x="277" y="360" width="134" height="166"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="9"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<state key="normal" image="numpad_9_default.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="highlighted" image="numpad_9_over.png" backgroundImage="numpad_over_background.png"/>
|
||||
</button>
|
||||
<button opaque="NO" clearsContextBeforeDrawing="NO" tag="3034" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="MdJ-SK-bIH" userLabel="*" customClass="UIDigitButton">
|
||||
<rect key="frame" x="-1" y="534" width="134" height="183"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Star"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<state key="normal" image="numpad_star_default.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="highlighted" image="numpad_star_over.png" backgroundImage="numpad_over_background.png"/>
|
||||
</button>
|
||||
<button opaque="NO" clearsContextBeforeDrawing="NO" tag="3035" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="49E-lz-Dx1" userLabel="0" customClass="UIDigitButton">
|
||||
<rect key="frame" x="138" y="534" width="134" height="183"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="0"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<state key="normal" image="numpad_0_default.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="highlighted" image="numpad_0_over.png" backgroundImage="numpad_over_background.png"/>
|
||||
</button>
|
||||
<button opaque="NO" clearsContextBeforeDrawing="NO" tag="3036" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="yiW-6Q-8Gn" userLabel="#" customClass="UIDigitButton">
|
||||
<rect key="frame" x="277" y="534" width="134" height="183"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Hash"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<state key="normal" image="numpad_hash_default.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="highlighted" image="numpad_hash_over.png" backgroundImage="numpad_over_background.png"/>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<point key="canvasLocation" x="42.028985507246382" y="111.16071428571428"/>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" id="YAs-im-wvR" userLabel="iphone6MetricsView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<view tag="1" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Czn-ec-dh8">
|
||||
<rect key="frame" x="0.0" y="42" width="414" height="854"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<button opaque="NO" tag="2" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="oAv-Cz-FaR" userLabel="headerBar">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="66"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration">
|
||||
<accessibilityTraits key="traits" button="YES" notEnabled="YES"/>
|
||||
</accessibility>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="27"/>
|
||||
<state key="normal" title="OUTGOING CALL" backgroundImage="color_F.png">
|
||||
<color key="titleColor" red="0.98766469955444336" green="0.27512490749359131" blue="0.029739789664745331" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
</button>
|
||||
<label opaque="NO" userInteractionEnabled="NO" tag="5" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="john.doe@sip.linphone.org" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="8" translatesAutoresizingMaskIntoConstraints="NO" id="Fj8-Pu-ShI" userLabel="addressLabel">
|
||||
<rect key="frame" x="185" y="426" width="228" height="84"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="18"/>
|
||||
<color key="textColor" red="0.98766469955444336" green="0.27512490749359131" blue="0.029739789664745331" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" tag="4" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="John Doe" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="19" translatesAutoresizingMaskIntoConstraints="NO" id="ubQ-ZN-AhT" userLabel="nameLabel">
|
||||
<rect key="frame" x="185" y="310" width="229" height="95"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="33"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<imageView userInteractionEnabled="NO" tag="6" contentMode="scaleAspectFit" fixedFrame="YES" image="avatar.png" translatesAutoresizingMaskIntoConstraints="NO" id="1ZH-n6-QZ0" userLabel="avatarImage" customClass="UIRoundedImageView">
|
||||
<rect key="frame" x="68" y="74" width="112" height="701"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Contact avatar">
|
||||
<accessibilityTraits key="traits" image="YES" notEnabled="YES"/>
|
||||
<bool key="isElement" value="YES"/>
|
||||
</accessibility>
|
||||
</imageView>
|
||||
<view hidden="YES" tag="37" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="EaW-SR-bqv" userLabel="routesView">
|
||||
<rect key="frame" x="311" y="593" width="104" height="198"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" tag="38" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="color_F.png" translatesAutoresizingMaskIntoConstraints="NO" id="Tvo-Jg-0h8" userLabel="backgroundColor">
|
||||
<rect key="frame" x="0.0" y="0.0" width="104" height="198"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
</imageView>
|
||||
<button opaque="NO" tag="39" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" adjustsImageWhenDisabled="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="N7s-YH-dJQ" userLabel="routesBluetoothButton" customClass="UIBluetoothButton">
|
||||
<rect key="frame" x="0.0" y="0.0" width="104" height="66"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Bluetooth"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="13"/>
|
||||
<inset key="titleEdgeInsets" minX="0.0" minY="28" maxX="0.0" maxY="0.0"/>
|
||||
<state key="normal" image="route_bluetooth_default.png">
|
||||
<color key="titleColor" red="0.19582480192184448" green="0.21824681758880615" blue="0.24259182810783386" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="disabled" image="route_bluetooth_disabled.png">
|
||||
<color key="titleColor" red="0.58667385578155518" green="0.64151597023010254" blue="0.69586050510406494" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="selected" image="route_bluetooth_selected.png"/>
|
||||
<state key="highlighted" backgroundImage="color_E.png">
|
||||
<color key="titleColor" red="0.76000285148620605" green="0.21515019237995148" blue="0.12376989424228668" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
</button>
|
||||
<button opaque="NO" tag="40" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" adjustsImageWhenDisabled="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="bg7-Cv-tyO" userLabel="routesEarpieceButton">
|
||||
<rect key="frame" x="0.0" y="66" width="104" height="66"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Receiver"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="13"/>
|
||||
<inset key="titleEdgeInsets" minX="0.0" minY="28" maxX="0.0" maxY="0.0"/>
|
||||
<state key="normal" image="route_earpiece_default.png">
|
||||
<color key="titleColor" red="0.19582480192184448" green="0.21824681758880615" blue="0.24259182810783386" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="disabled" image="route_earpiece_disabled.png">
|
||||
<color key="titleColor" red="0.58667385578155518" green="0.64151597023010254" blue="0.69586050510406494" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="selected" image="route_earpiece_selected.png"/>
|
||||
<state key="highlighted" backgroundImage="color_E.png">
|
||||
<color key="titleColor" red="0.76000285148620605" green="0.21515019237995148" blue="0.12376989424228668" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
</button>
|
||||
<button opaque="NO" tag="41" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" adjustsImageWhenDisabled="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="yd8-ed-g8u" userLabel="routesSpeakerButton" customClass="UISpeakerButton">
|
||||
<rect key="frame" x="0.0" y="132" width="104" height="66"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Speaker"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="13"/>
|
||||
<inset key="titleEdgeInsets" minX="0.0" minY="28" maxX="0.0" maxY="0.0"/>
|
||||
<state key="normal" image="route_speaker_default.png">
|
||||
<color key="titleColor" red="0.19582480192184448" green="0.21824681758880615" blue="0.24259182810783386" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="disabled" image="route_speaker_disabled.png">
|
||||
<color key="titleColor" red="0.58667385578155518" green="0.64151597023010254" blue="0.69586050510406494" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="selected" image="route_speaker_selected.png"/>
|
||||
<state key="highlighted" backgroundImage="color_E.png">
|
||||
<color key="titleColor" red="0.76000285148620605" green="0.21515019237995148" blue="0.12376989424228668" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
</button>
|
||||
</subviews>
|
||||
</view>
|
||||
<view tag="7" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="vJ1-A8-eFV" userLabel="tabBar">
|
||||
<rect key="frame" x="0.0" y="791" width="414" height="63"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" tag="8" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="color_F.png" translatesAutoresizingMaskIntoConstraints="NO" id="eYb-yI-yVB" userLabel="backgroundColor">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="63"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||
</imageView>
|
||||
<button opaque="NO" tag="11" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="jfG-HJ-FPI" userLabel="declineButton">
|
||||
<rect key="frame" x="0.0" y="0.0" width="207" height="63"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Decline"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
|
||||
<state key="normal" image="call_hangup_default.png" backgroundImage="color_D.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="disabled" image="call_hangup_disabled.png"/>
|
||||
<state key="highlighted" backgroundImage="color_I.png"/>
|
||||
<connections>
|
||||
<action selector="onDeclineClick:" destination="-1" eventType="touchUpInside" id="voJ-Cd-XHg"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" tag="9" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="jLg-1u-ulZ" userLabel="microButton" customClass="UIMutedMicroButton">
|
||||
<rect key="frame" x="207" y="0.0" width="104" height="63"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Accept"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
|
||||
<state key="normal" image="micro_default.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="disabled" image="micro_disabled.png"/>
|
||||
<state key="selected" image="micro_selected.png"/>
|
||||
<state key="highlighted" backgroundImage="color_E.png"/>
|
||||
</button>
|
||||
<button hidden="YES" opaque="NO" tag="54" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="61B-DK-jZ6" userLabel="routesButton" customClass="UIToggleButton">
|
||||
<rect key="frame" x="311" y="0.0" width="104" height="63"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Route"/>
|
||||
<inset key="titleEdgeInsets" minX="0.0" minY="38" maxX="0.0" maxY="0.0"/>
|
||||
<state key="normal" image="routes_default.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="disabled" image="routes_disabled.png"/>
|
||||
<state key="selected" image="routes_selected.png"/>
|
||||
<state key="highlighted" backgroundImage="color_E.png"/>
|
||||
</button>
|
||||
<button opaque="NO" tag="10" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="5CY-aN-NLX" userLabel="speakerButton" customClass="UISpeakerButton">
|
||||
<rect key="frame" x="311" y="0.0" width="104" height="63"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Accept"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
|
||||
<state key="normal" image="speaker_default.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="disabled" image="speaker_disabled.png"/>
|
||||
<state key="selected" image="speaker_selected.png"/>
|
||||
<state key="highlighted" backgroundImage="color_E.png"/>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<point key="canvasLocation" x="502.17391304347831" y="39.84375"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="avatar.png" width="414.39999389648438" height="414.39999389648438"/>
|
||||
<image name="call_hangup_default.png" width="67.199996948242188" height="58.400001525878906"/>
|
||||
<image name="call_hangup_disabled.png" width="67.199996948242188" height="58.400001525878906"/>
|
||||
<image name="color_A.png" width="2" height="2"/>
|
||||
<image name="color_C.png" width="2" height="2"/>
|
||||
<image name="color_D.png" width="2" height="2"/>
|
||||
<image name="color_E.png" width="2" height="2"/>
|
||||
<image name="color_F.png" width="2" height="2"/>
|
||||
<image name="color_I.png" width="2" height="2"/>
|
||||
<image name="dialer_alt_back.png" width="42.400001525878906" height="42.400001525878906"/>
|
||||
<image name="footer_dialer_default.png" width="44" height="44"/>
|
||||
<image name="footer_dialer_disabled.png" width="44" height="44"/>
|
||||
<image name="micro_default.png" width="47.200000762939453" height="60"/>
|
||||
<image name="micro_disabled.png" width="47.200000762939453" height="60"/>
|
||||
<image name="micro_selected.png" width="47.200000762939453" height="60"/>
|
||||
<image name="numpad_0_default.png" width="84.800003051757812" height="79.199996948242188"/>
|
||||
<image name="numpad_0_over.png" width="84.800003051757812" height="79.199996948242188"/>
|
||||
<image name="numpad_1_default.png" width="84.800003051757812" height="77.599998474121094"/>
|
||||
<image name="numpad_1_over.png" width="84.800003051757812" height="77.599998474121094"/>
|
||||
<image name="numpad_2_default.png" width="84.800003051757812" height="79.199996948242188"/>
|
||||
<image name="numpad_2_over.png" width="84.800003051757812" height="79.199996948242188"/>
|
||||
<image name="numpad_3_default.png" width="84.800003051757812" height="79.199996948242188"/>
|
||||
<image name="numpad_3_over.png" width="84.800003051757812" height="79.199996948242188"/>
|
||||
<image name="numpad_4_default.png" width="84.800003051757812" height="77.599998474121094"/>
|
||||
<image name="numpad_4_over.png" width="84.800003051757812" height="77.599998474121094"/>
|
||||
<image name="numpad_5_default.png" width="84.800003051757812" height="77.599998474121094"/>
|
||||
<image name="numpad_5_over.png" width="84.800003051757812" height="77.599998474121094"/>
|
||||
<image name="numpad_6_default.png" width="84.800003051757812" height="79.199996948242188"/>
|
||||
<image name="numpad_6_over.png" width="84.800003051757812" height="79.199996948242188"/>
|
||||
<image name="numpad_7_default.png" width="84.800003051757812" height="77.599998474121094"/>
|
||||
<image name="numpad_7_over.png" width="84.800003051757812" height="77.599998474121094"/>
|
||||
<image name="numpad_8_default.png" width="84.800003051757812" height="79.199996948242188"/>
|
||||
<image name="numpad_8_over.png" width="84.800003051757812" height="79.199996948242188"/>
|
||||
<image name="numpad_9_default.png" width="84.800003051757812" height="79.199996948242188"/>
|
||||
<image name="numpad_9_over.png" width="84.800003051757812" height="79.199996948242188"/>
|
||||
<image name="numpad_hash_default.png" width="84.800003051757812" height="78.400001525878906"/>
|
||||
<image name="numpad_hash_over.png" width="84.800003051757812" height="78.400001525878906"/>
|
||||
<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"/>
|
||||
<image name="route_bluetooth_default.png" width="26.399999618530273" height="41.599998474121094"/>
|
||||
<image name="route_bluetooth_disabled.png" width="26.399999618530273" height="41.599998474121094"/>
|
||||
<image name="route_bluetooth_selected.png" width="26.399999618530273" height="41.599998474121094"/>
|
||||
<image name="route_earpiece_default.png" width="37.599998474121094" height="38.400001525878906"/>
|
||||
<image name="route_earpiece_disabled.png" width="37.599998474121094" height="38.400001525878906"/>
|
||||
<image name="route_earpiece_selected.png" width="38.400001525878906" height="38.400001525878906"/>
|
||||
<image name="route_speaker_default.png" width="44" height="41.599998474121094"/>
|
||||
<image name="route_speaker_disabled.png" width="44" height="41.599998474121094"/>
|
||||
<image name="route_speaker_selected.png" width="44" height="41.599998474121094"/>
|
||||
<image name="routes_default.png" width="60" height="40.799999237060547"/>
|
||||
<image name="routes_disabled.png" width="60" height="40.799999237060547"/>
|
||||
<image name="routes_selected.png" width="60" height="40.799999237060547"/>
|
||||
<image name="speaker_default.png" width="44" height="41.599998474121094"/>
|
||||
<image name="speaker_disabled.png" width="44" height="41.599998474121094"/>
|
||||
<image name="speaker_selected.png" width="44" height="41.599998474121094"/>
|
||||
<systemColor name="systemBackgroundColor">
|
||||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</systemColor>
|
||||
</resources>
|
||||
</document>
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,9 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19162" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" 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="19144"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
|
|
@ -21,7 +21,6 @@
|
|||
<outlet property="ephemeralndicator" destination="LN7-ci-kNn" id="hqU-98-qkJ"/>
|
||||
<outlet property="imagesCollectionView" destination="JGQ-p2-HCX" id="6dt-1f-jpa"/>
|
||||
<outlet property="imagesView" destination="3qd-ys-t2L" id="f9L-FU-PMI"/>
|
||||
<outlet property="infoButton" destination="Vqb-Un-4xv" id="pa1-Iz-5QQ"/>
|
||||
<outlet property="landscapeView" destination="VoU-7Q-fgp" id="iRJ-sh-thF"/>
|
||||
<outlet property="listSwipeGestureRecognizer" destination="dzw-n4-l9i" id="JVP-Vl-lIa"/>
|
||||
<outlet property="listTapGestureRecognizer" destination="tkk-Tm-A7C" id="gqU-iJ-RGm"/>
|
||||
|
|
@ -185,22 +184,6 @@
|
|||
<action selector="onSelectionToggle:" destination="29" eventType="touchUpInside" id="eP5-bU-LEA"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button hidden="YES" opaque="NO" tag="10" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" reversesTitleShadowWhenHighlighted="YES" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Vqb-Un-4xv" userLabel="infoButton" customClass="UIInterfaceStyleButton">
|
||||
<rect key="frame" x="248" y="0.0" width="83" height="66"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Select all"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<inset key="titleEdgeInsets" minX="0.0" minY="18" maxX="0.0" maxY="0.0"/>
|
||||
<state key="normal" image="chat_group_informations.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="disabled" image="select_all_disabled.png"/>
|
||||
<state key="selected" image="select_all_default.png"/>
|
||||
<state key="highlighted" backgroundImage="color_E.png"/>
|
||||
<connections>
|
||||
<action selector="onInfoClick:" destination="-1" eventType="touchUpInside" id="VfD-K7-V15"/>
|
||||
</connections>
|
||||
</button>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="5" contentMode="left" fixedFrame="YES" text="addresses" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ncq-Zc-X6j" userLabel="participantsLabel">
|
||||
<rect key="frame" x="75" y="36" width="160" height="25"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
|
|
@ -507,22 +490,6 @@
|
|||
<action selector="onToggleMenu:" destination="-1" eventType="touchUpInside" id="vYL-BQ-UTF"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button hidden="YES" opaque="NO" tag="10" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" reversesTitleShadowWhenHighlighted="YES" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="uVy-PC-gn9" userLabel="infoButton" customClass="UIIconButton">
|
||||
<rect key="frame" x="248" y="0.0" width="37" height="66"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Select all"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<inset key="titleEdgeInsets" minX="0.0" minY="18" maxX="0.0" maxY="0.0"/>
|
||||
<state key="normal" image="chat_group_informations.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="disabled" image="select_all_disabled.png"/>
|
||||
<state key="selected" image="select_all_default.png"/>
|
||||
<state key="highlighted" backgroundImage="color_E.png"/>
|
||||
<connections>
|
||||
<action selector="onInfoClick:" destination="-1" eventType="touchUpInside" id="HQo-52-fEq"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" tag="6" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="wag-QV-oUD" userLabel="callButton" customClass="UIIconButton">
|
||||
<rect key="frame" x="248" y="0.0" width="37" height="66"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
|
||||
|
|
@ -721,7 +688,6 @@
|
|||
<image name="chat_attachment_default.png" width="65.599998474121094" height="65.599998474121094"/>
|
||||
<image name="chat_attachment_disabled.png" width="65.599998474121094" height="65.599998474121094"/>
|
||||
<image name="chat_attachment_over.png" width="65.599998474121094" height="65.599998474121094"/>
|
||||
<image name="chat_group_informations.png" width="38" height="38"/>
|
||||
<image name="chat_send_default.png" width="65.599998474121094" height="65.599998474121094"/>
|
||||
<image name="chat_send_disabled.png" width="65.599998474121094" height="65.599998474121094"/>
|
||||
<image name="chat_send_over.png" width="65.599998474121094" height="65.599998474121094"/>
|
||||
|
|
@ -731,7 +697,7 @@
|
|||
<image name="delete_disabled.png" width="34.400001525878906" height="44.799999237060547"/>
|
||||
<image name="deselect_all.png" width="43.200000762939453" height="43.200000762939453"/>
|
||||
<image name="ephemeral_messages_color_A.png" width="136" height="158.39999389648438"/>
|
||||
<image name="more_menu_default.png" width="7.1999998092651367" height="9.3599996566772461"/>
|
||||
<image name="more_menu_default.png" width="118.40000152587891" height="118.40000152587891"/>
|
||||
<image name="security_1_indicator.png" width="27.5" height="32.5"/>
|
||||
<image name="select_all_default.png" width="43.200000762939453" height="43.200000762939453"/>
|
||||
<image name="select_all_disabled.png" width="43.200000762939453" height="43.200000762939453"/>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" 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="17703"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
|
|
|
|||
|
|
@ -1,13 +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" colorMatched="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19455" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19454"/>
|
||||
<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="HistoryListView">
|
||||
<connections>
|
||||
<outlet property="allButton" destination="4" id="27"/>
|
||||
<outlet property="conferenceButton" destination="VWZ-Nd-W2s" id="wy8-oW-FqP"/>
|
||||
<outlet property="missedButton" destination="5" id="28"/>
|
||||
<outlet property="selectedButtonImage" destination="o8E-gw-vhI" id="hNf-FA-7aQ"/>
|
||||
<outlet property="tableController" destination="18" id="26"/>
|
||||
|
|
@ -29,7 +33,7 @@
|
|||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="38" userLabel="switchView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="150" height="66"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="225" height="66"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="4" userLabel="allButton" customClass="UIInterfaceStyleButton">
|
||||
|
|
@ -49,7 +53,7 @@
|
|||
</button>
|
||||
<button opaque="NO" contentMode="bottom" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="5" userLabel="missedButton" customClass="UIInterfaceStyleButton">
|
||||
<rect key="frame" x="75" y="0.0" width="75" height="66"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Missed contacts filter"/>
|
||||
<state key="normal" image="history_missed_default.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
|
|
@ -61,6 +65,21 @@
|
|||
<action selector="onMissedClick:" destination="-1" eventType="touchUpInside" id="30"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleAspectFit" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="VWZ-Nd-W2s" userLabel="conferenceButton" customClass="UIInterfaceStyleButton">
|
||||
<rect key="frame" x="150" y="0.0" width="75" height="66"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Missed contacts filter"/>
|
||||
<inset key="imageEdgeInsets" minX="10" minY="5" maxX="10" maxY="5"/>
|
||||
<state key="normal" image="voip_conference_new.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="disabled" image="history_missed_disabled.png"/>
|
||||
<state key="selected" image="voip_conference_new_selected.png"/>
|
||||
<state key="highlighted" backgroundImage="color_E.png"/>
|
||||
<connections>
|
||||
<action selector="onConferenceClick:" destination="-1" eventType="touchUpInside" id="9hJ-lr-NB7"/>
|
||||
</connections>
|
||||
</button>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="color_A.png" translatesAutoresizingMaskIntoConstraints="NO" id="o8E-gw-vhI" userLabel="selectedButtonImage">
|
||||
<rect key="frame" x="0.0" y="63" width="75" height="3"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES"/>
|
||||
|
|
@ -127,12 +146,12 @@
|
|||
</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>
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" fixedFrame="YES" alwaysBounceVertical="YES" style="plain" separatorStyle="default" allowsSelectionDuringEditing="YES" allowsMultipleSelectionDuringEditing="YES" rowHeight="44" sectionHeaderHeight="35" sectionFooterHeight="1" translatesAutoresizingMaskIntoConstraints="NO" id="17" userLabel="tableView">
|
||||
<rect key="frame" x="0.0" y="66" width="375" height="493"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<inset key="scrollIndicatorInsets" minX="0.0" minY="0.0" maxX="0.0" maxY="10"/>
|
||||
<color key="separatorColor" red="0.67030966281890869" green="0.71867996454238892" blue="0.75078284740447998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<connections>
|
||||
|
|
@ -143,7 +162,7 @@
|
|||
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="No call in your history" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="xtr-Fp-60Z" userLabel="emptyTableLabel">
|
||||
<rect key="frame" x="0.0" y="66" width="375" height="493"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
|
|
@ -184,5 +203,13 @@
|
|||
<image name="history_missed_selected.png" width="52.799999237060547" height="52.799999237060547"/>
|
||||
<image name="select_all_default.png" width="43.200000762939453" height="43.200000762939453"/>
|
||||
<image name="select_all_disabled.png" width="43.200000762939453" height="43.200000762939453"/>
|
||||
<image name="voip_conference_new.png" width="97.599998474121094" height="97.599998474121094"/>
|
||||
<image name="voip_conference_new_selected.png" width="97.599998474121094" height="97.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>
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface CallConferenceTableView : UITableViewController
|
||||
|
||||
- (void)update;
|
||||
|
||||
@end
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#import "linphoneapp-Swift.h"
|
||||
#import "CallConferenceTableView.h"
|
||||
#import "UICallConferenceCell.h"
|
||||
#import "LinphoneManager.h"
|
||||
#import "Utils.h"
|
||||
#import "linphoneapp-Swift.h"
|
||||
|
||||
@implementation CallConferenceTableView
|
||||
|
||||
#pragma mark - UI change
|
||||
|
||||
- (void)update {
|
||||
[self.tableView reloadData];
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDataSource Functions
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
NSString *kCellId = NSStringFromClass(UICallConferenceCell.class);
|
||||
UICallConferenceCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellId];
|
||||
if (cell == nil) {
|
||||
cell = [[UICallConferenceCell alloc] initWithIdentifier:kCellId];
|
||||
}
|
||||
LinphoneConference *c = [CallManager.instance getConference];
|
||||
if (c != nil) {
|
||||
[cell setParticipant:bctbx_list_nth_data(linphone_conference_get_participant_list(c),(int)indexPath.row)];
|
||||
}
|
||||
cell.contentView.userInteractionEnabled = NO;
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
return [CallManager.instance getConference] ? linphone_conference_get_participant_count([CallManager.instance getConference]) : 0;
|
||||
}
|
||||
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
|
||||
return 1;
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
|
||||
return 1e-5;
|
||||
}
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
|
||||
return 1e-5;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "UICompositeView.h"
|
||||
#import "TPMultiLayoutViewController.h"
|
||||
#import "UIRoundedImageView.h"
|
||||
#include "LinphoneManager.h"
|
||||
|
||||
@protocol IncomingCallViewDelegate <NSObject>
|
||||
|
||||
- (void)incomingCallAccepted:(LinphoneCall *)call evenWithVideo:(BOOL)video;
|
||||
- (void)incomingCallDeclined:(LinphoneCall *)call;
|
||||
- (void)incomingCallAborted:(LinphoneCall *)call;
|
||||
|
||||
@end
|
||||
|
||||
@interface CallIncomingView : TPMultiLayoutViewController <UICompositeViewDelegate> {
|
||||
}
|
||||
|
||||
@property(nonatomic) Boolean earlyMedia;
|
||||
|
||||
@property(weak, nonatomic) IBOutlet UILabel *nameLabel;
|
||||
@property(nonatomic, strong) IBOutlet UILabel *addressLabel;
|
||||
@property(nonatomic, strong) IBOutlet UIRoundedImageView *avatarImage;
|
||||
@property(nonatomic, assign) LinphoneCall *call;
|
||||
@property(nonatomic, strong) id<IncomingCallViewDelegate> delegate;
|
||||
@property(weak, nonatomic) IBOutlet UIView *tabVideoBar;
|
||||
@property(weak, nonatomic) IBOutlet UIView *tabBar;
|
||||
@property (weak, nonatomic) IBOutlet UIView *earlyMediaView;
|
||||
|
||||
|
||||
- (IBAction)onAcceptClick:(id)event;
|
||||
- (IBAction)onDeclineClick:(id)event;
|
||||
- (IBAction)onAcceptAudioOnlyClick:(id)sender;
|
||||
|
||||
@end
|
||||
|
|
@ -1,150 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#import "CallIncomingView.h"
|
||||
#import "LinphoneManager.h"
|
||||
#import "FastAddressBook.h"
|
||||
#import "PhoneMainView.h"
|
||||
#import "Utils.h"
|
||||
|
||||
@implementation CallIncomingView
|
||||
|
||||
#pragma mark - ViewController Functions
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
[NSNotificationCenter.defaultCenter addObserver:self
|
||||
selector:@selector(callUpdateEvent:)
|
||||
name:kLinphoneCallUpdate
|
||||
object:nil];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
[super viewWillDisappear:animated];
|
||||
[NSNotificationCenter.defaultCenter removeObserver:self name:kLinphoneCallUpdate object:nil];
|
||||
_call = NULL;
|
||||
}
|
||||
|
||||
#pragma mark - UICompositeViewDelegate Functions
|
||||
|
||||
static UICompositeViewDescription *compositeDescription = nil;
|
||||
|
||||
+ (UICompositeViewDescription *)compositeViewDescription {
|
||||
if (compositeDescription == nil) {
|
||||
compositeDescription = [[UICompositeViewDescription alloc] init:self.class
|
||||
statusBar:StatusBarView.class
|
||||
tabBar:nil
|
||||
sideMenu:CallSideMenuView.class
|
||||
fullscreen:false
|
||||
isLeftFragment:YES
|
||||
fragmentWith:nil];
|
||||
compositeDescription.darkBackground = true;
|
||||
}
|
||||
return compositeDescription;
|
||||
}
|
||||
|
||||
- (UICompositeViewDescription *)compositeViewDescription {
|
||||
return self.class.compositeViewDescription;
|
||||
}
|
||||
|
||||
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
|
||||
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
|
||||
if (_earlyMedia && [LinphoneManager.instance lpConfigBoolForKey:@"pref_accept_early_media"] && linphone_core_get_calls_nb(LC) < 2) {
|
||||
_earlyMediaView.hidden = NO;
|
||||
linphone_core_set_native_video_window_id(LC, (__bridge void *)(_earlyMediaView));
|
||||
}
|
||||
if (_call) {
|
||||
[self update];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Event Functions
|
||||
|
||||
- (void)callUpdateEvent:(NSNotification *)notif {
|
||||
LinphoneCall *acall = [[notif.userInfo objectForKey:@"call"] pointerValue];
|
||||
LinphoneCallState astate = [[notif.userInfo objectForKey:@"state"] intValue];
|
||||
[self callUpdate:acall state:astate];
|
||||
}
|
||||
|
||||
- (void)callUpdate:(LinphoneCall *)acall state:(LinphoneCallState)astate {
|
||||
if (_call == acall && (astate == LinphoneCallEnd || astate == LinphoneCallError)) {
|
||||
[_delegate incomingCallAborted:_call];
|
||||
} else if ([LinphoneManager.instance lpConfigBoolForKey:@"auto_answer"]) {
|
||||
LinphoneCallState state = linphone_call_get_state(_call);
|
||||
if (state == LinphoneCallIncomingReceived) {
|
||||
LOGI(@"Auto answering call");
|
||||
[self onAcceptClick:nil];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)update {
|
||||
const LinphoneAddress *addr = linphone_call_get_remote_address(_call);
|
||||
[ContactDisplay setDisplayNameLabel:_nameLabel forAddress:addr withAddressLabel:_addressLabel];
|
||||
char *uri = linphone_address_as_string_uri_only(addr);
|
||||
ms_free(uri);
|
||||
[_avatarImage setImage:[FastAddressBook imageForAddress:addr] bordered:YES withRoundedRadius:YES];
|
||||
|
||||
_tabBar.hidden = linphone_call_params_video_enabled(linphone_call_get_remote_params(_call));
|
||||
_tabVideoBar.hidden = !_tabBar.hidden;
|
||||
}
|
||||
|
||||
#pragma mark - Property Functions
|
||||
static void hideSpinner(LinphoneCall *call, void *user_data) {
|
||||
CallIncomingView *thiz = (__bridge CallIncomingView *)user_data;
|
||||
thiz.earlyMedia = TRUE;
|
||||
thiz.earlyMediaView.hidden = NO;
|
||||
linphone_core_set_native_video_window_id(LC, (__bridge void *)(thiz.earlyMediaView));
|
||||
}
|
||||
|
||||
- (void)setCall:(LinphoneCall *)call {
|
||||
_call = call;
|
||||
_earlyMedia = FALSE;
|
||||
if ([LinphoneManager.instance lpConfigBoolForKey:@"pref_accept_early_media"] && linphone_core_get_calls_nb(LC) < 2) {
|
||||
linphone_call_accept_early_media(_call);
|
||||
// linphone_call_params_get_used_video_codec return 0 if no video stream enabled
|
||||
if (linphone_call_params_get_used_video_codec(linphone_call_get_current_params(_call))) {
|
||||
linphone_call_set_next_video_frame_decoded_callback(call, hideSpinner, (__bridge void *)(self));
|
||||
}
|
||||
} else {
|
||||
_earlyMediaView.hidden = YES;
|
||||
}
|
||||
|
||||
[self update];
|
||||
[self callUpdate:_call state:linphone_call_get_state(call)];
|
||||
}
|
||||
|
||||
#pragma mark - Action Functions
|
||||
|
||||
- (IBAction)onAcceptClick:(id)event {
|
||||
[_delegate incomingCallAccepted:_call evenWithVideo:YES];
|
||||
}
|
||||
|
||||
- (IBAction)onDeclineClick:(id)event {
|
||||
[_delegate incomingCallDeclined:_call];
|
||||
}
|
||||
|
||||
- (IBAction)onAcceptAudioOnlyClick:(id)sender {
|
||||
[_delegate incomingCallAccepted:_call evenWithVideo:NO];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "UICompositeView.h"
|
||||
#import "TPMultiLayoutViewController.h"
|
||||
#include "linphone/linphonecore.h"
|
||||
#import "UIRoundedImageView.h"
|
||||
#import "UIDigitButton.h"
|
||||
|
||||
|
||||
@interface CallOutgoingView : TPMultiLayoutViewController <UICompositeViewDelegate> {
|
||||
}
|
||||
@property(weak, nonatomic) IBOutlet UIRoundedImageView *avatarImage;
|
||||
@property(weak, nonatomic) IBOutlet UILabel *nameLabel;
|
||||
@property(weak, nonatomic) IBOutlet UISpeakerButton *speakerButton;
|
||||
@property(weak, nonatomic) IBOutlet UILabel *addressLabel;
|
||||
@property(weak, nonatomic) IBOutlet UIToggleButton *routesButton;
|
||||
@property(weak, nonatomic) IBOutlet UIView *routesView;
|
||||
@property(weak, nonatomic) IBOutlet UIBluetoothButton *routesBluetoothButton;
|
||||
@property(weak, nonatomic) IBOutlet UIButton *routesEarpieceButton;
|
||||
@property(weak, nonatomic) IBOutlet UISpeakerButton *routesSpeakerButton;
|
||||
@property(weak, nonatomic) IBOutlet UIMutedMicroButton *microButton;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *declineButton;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIToggleButton *numpadButton;
|
||||
@property (strong, nonatomic) IBOutlet UIView *numpadView;
|
||||
@property(nonatomic, strong) IBOutlet UIDigitButton *oneButton;
|
||||
@property(nonatomic, strong) IBOutlet UIDigitButton *twoButton;
|
||||
@property(nonatomic, strong) IBOutlet UIDigitButton *threeButton;
|
||||
@property(nonatomic, strong) IBOutlet UIDigitButton *fourButton;
|
||||
@property(nonatomic, strong) IBOutlet UIDigitButton *fiveButton;
|
||||
@property(nonatomic, strong) IBOutlet UIDigitButton *sixButton;
|
||||
@property(nonatomic, strong) IBOutlet UIDigitButton *sevenButton;
|
||||
@property(nonatomic, strong) IBOutlet UIDigitButton *eightButton;
|
||||
@property(nonatomic, strong) IBOutlet UIDigitButton *nineButton;
|
||||
@property(nonatomic, strong) IBOutlet UIDigitButton *starButton;
|
||||
@property(nonatomic, strong) IBOutlet UIDigitButton *zeroButton;
|
||||
@property(nonatomic, strong) IBOutlet UIDigitButton *hashButton;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *declineButton_earlyMedia;
|
||||
|
||||
- (IBAction)onDeclineClick:(id)sender;
|
||||
|
||||
@end
|
||||
|
|
@ -1,268 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#import "linphoneapp-Swift.h"
|
||||
#import "CallOutgoingView.h"
|
||||
#import "PhoneMainView.h"
|
||||
|
||||
@implementation CallOutgoingView
|
||||
|
||||
#pragma mark - UICompositeViewDelegate Functions
|
||||
|
||||
static UICompositeViewDescription *compositeDescription = nil;
|
||||
|
||||
+ (UICompositeViewDescription *)compositeViewDescription {
|
||||
if (compositeDescription == nil) {
|
||||
compositeDescription = [[UICompositeViewDescription alloc] init:self.class
|
||||
statusBar:StatusBarView.class
|
||||
tabBar:nil
|
||||
sideMenu:CallSideMenuView.class
|
||||
fullscreen:false
|
||||
isLeftFragment:NO
|
||||
fragmentWith:nil];
|
||||
|
||||
compositeDescription.darkBackground = true;
|
||||
}
|
||||
return compositeDescription;
|
||||
}
|
||||
|
||||
- (UICompositeViewDescription *)compositeViewDescription {
|
||||
return self.class.compositeViewDescription;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
_routesEarpieceButton.enabled = !IPAD;
|
||||
|
||||
[_zeroButton setDigit:'0'];
|
||||
[_zeroButton setDtmf:true];
|
||||
[_oneButton setDigit:'1'];
|
||||
[_oneButton setDtmf:true];
|
||||
[_twoButton setDigit:'2'];
|
||||
[_twoButton setDtmf:true];
|
||||
[_threeButton setDigit:'3'];
|
||||
[_threeButton setDtmf:true];
|
||||
[_fourButton setDigit:'4'];
|
||||
[_fourButton setDtmf:true];
|
||||
[_fiveButton setDigit:'5'];
|
||||
[_fiveButton setDtmf:true];
|
||||
[_sixButton setDigit:'6'];
|
||||
[_sixButton setDtmf:true];
|
||||
[_sevenButton setDigit:'7'];
|
||||
[_sevenButton setDtmf:true];
|
||||
[_eightButton setDigit:'8'];
|
||||
[_eightButton setDtmf:true];
|
||||
[_nineButton setDigit:'9'];
|
||||
[_nineButton setDtmf:true];
|
||||
[_starButton setDigit:'*'];
|
||||
[_starButton setDtmf:true];
|
||||
[_hashButton setDigit:'#'];
|
||||
[_hashButton setDtmf:true];
|
||||
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
[NSNotificationCenter.defaultCenter addObserver:self
|
||||
selector:@selector(bluetoothAvailabilityUpdateEvent:)
|
||||
name:kLinphoneBluetoothAvailabilityUpdate
|
||||
object:nil];
|
||||
|
||||
[NSNotificationCenter.defaultCenter addObserver:self
|
||||
selector:@selector(callUpdateEvent:)
|
||||
name:kLinphoneCallUpdate
|
||||
object:nil];
|
||||
|
||||
LinphoneCall *call = linphone_core_get_current_call(LC);
|
||||
if (!call) {
|
||||
return;
|
||||
}
|
||||
|
||||
const LinphoneAddress *addr = linphone_call_get_remote_address(call);
|
||||
[ContactDisplay setDisplayNameLabel:_nameLabel forAddress:addr withAddressLabel:_addressLabel];
|
||||
char *uri = linphone_address_as_string_uri_only(addr);
|
||||
ms_free(uri);
|
||||
[_avatarImage setImage:[FastAddressBook imageForAddress:addr] bordered:NO withRoundedRadius:YES];
|
||||
|
||||
[self hideSpeaker: [CallManager.instance isBluetoothAvailable]];
|
||||
|
||||
[_speakerButton update];
|
||||
[_microButton update];
|
||||
[_routesButton update];
|
||||
[self hidePad:TRUE animated:FALSE];
|
||||
[self callUpdate:call state:linphone_call_get_state(call) animated:true];
|
||||
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
[super viewDidAppear:animated];
|
||||
// if there is no call (for whatever reason), we must wait viewDidAppear method
|
||||
// before popping current view, because UICompositeView cannot handle view change
|
||||
// directly in viewWillAppear (this would lead to crash in deallocated memory - easily
|
||||
// reproductible on iPad mini).
|
||||
if (!linphone_core_get_current_call(LC)) {
|
||||
[PhoneMainView.instance popCurrentView];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
[super viewWillDisappear:animated];
|
||||
[NSNotificationCenter.defaultCenter removeObserver:self];
|
||||
}
|
||||
|
||||
- (IBAction)onRoutesBluetoothClick:(id)sender {
|
||||
[self hideRoutes:TRUE animated:TRUE];
|
||||
[CallManager.instance changeRouteToBluetooth];
|
||||
}
|
||||
|
||||
- (IBAction)onRoutesEarpieceClick:(id)sender {
|
||||
[self hideRoutes:TRUE animated:TRUE];
|
||||
[CallManager.instance changeRouteToDefault];
|
||||
}
|
||||
|
||||
- (IBAction)onRoutesSpeakerClick:(id)sender {
|
||||
[self hideRoutes:TRUE animated:TRUE];
|
||||
[CallManager.instance changeRouteToSpeaker];
|
||||
}
|
||||
|
||||
- (IBAction)onRoutesClick:(id)sender {
|
||||
if ([_routesView isHidden]) {
|
||||
[self hideRoutes:FALSE animated:ANIMATED];
|
||||
} else {
|
||||
[self hideRoutes:TRUE animated:ANIMATED];
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)onNumpadClick:(id)sender {
|
||||
if ([_numpadView isHidden]) {
|
||||
[self hidePad:FALSE animated:ANIMATED];
|
||||
} else {
|
||||
[self hidePad:TRUE animated:ANIMATED];
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)onDeclineClick:(id)sender {
|
||||
LinphoneCall *call = linphone_core_get_current_call(LC);
|
||||
if (call) {
|
||||
[CallManager.instance terminateCallWithCall:call];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)hidePad:(BOOL)hidden animated:(BOOL)animated {
|
||||
if (hidden) {
|
||||
[_numpadButton setOff];
|
||||
} else {
|
||||
[_numpadButton setOn];
|
||||
}
|
||||
if (hidden != _numpadView.hidden) {
|
||||
if (animated) {
|
||||
[self hideAnimation:hidden forView:_numpadView completion:nil];
|
||||
} else {
|
||||
[_numpadView setHidden:hidden];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)hideRoutes:(BOOL)hidden animated:(BOOL)animated {
|
||||
if (hidden) {
|
||||
[_routesButton setOff];
|
||||
} else {
|
||||
[_routesButton setOn];
|
||||
}
|
||||
|
||||
_routesBluetoothButton.selected = [CallManager.instance isBluetoothEnabled];
|
||||
_routesSpeakerButton.selected = [CallManager.instance isSpeakerEnabled];
|
||||
_routesEarpieceButton.selected = !_routesBluetoothButton.selected && !_routesSpeakerButton.selected;
|
||||
|
||||
if (hidden != _routesView.hidden) {
|
||||
[_routesView setHidden:hidden];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)hideSpeaker:(BOOL)hidden {
|
||||
_speakerButton.hidden = hidden;
|
||||
_routesButton.hidden = !hidden;
|
||||
}
|
||||
|
||||
#pragma mark - Event Functions
|
||||
|
||||
- (void)bluetoothAvailabilityUpdateEvent:(NSNotification *)notif {
|
||||
bool available = [[notif.userInfo objectForKey:@"available"] intValue];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self hideSpeaker:available];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)callUpdateEvent:(NSNotification *)notif {
|
||||
LinphoneCall *call = [[notif.userInfo objectForKey:@"call"] pointerValue];
|
||||
LinphoneCallState state = [[notif.userInfo objectForKey:@"state"] intValue];
|
||||
[self callUpdate:call state:state animated:TRUE];
|
||||
}
|
||||
|
||||
- (void)callUpdate:(LinphoneCall *)call state:(LinphoneCallState)state animated:(BOOL)animated {
|
||||
_declineButton_earlyMedia.hidden = linphone_call_get_state(call) != LinphoneCallStateOutgoingEarlyMedia;
|
||||
_declineButton.hidden = !_declineButton_earlyMedia.hidden;
|
||||
_numpadButton.hidden = _declineButton_earlyMedia.hidden;
|
||||
}
|
||||
|
||||
#pragma mark - Animation
|
||||
|
||||
- (void)hideAnimation:(BOOL)hidden forView:(UIView *)target completion:(void (^)(BOOL finished))completion {
|
||||
if (hidden) {
|
||||
int original_y = target.frame.origin.y;
|
||||
CGRect newFrame = target.frame;
|
||||
newFrame.origin.y = self.view.frame.size.height;
|
||||
[UIView animateWithDuration:0.5
|
||||
delay:0.0
|
||||
options:UIViewAnimationOptionCurveEaseIn
|
||||
animations:^{
|
||||
target.frame = newFrame;
|
||||
}
|
||||
completion:^(BOOL finished) {
|
||||
CGRect originFrame = target.frame;
|
||||
originFrame.origin.y = original_y;
|
||||
target.hidden = YES;
|
||||
target.frame = originFrame;
|
||||
if (completion)
|
||||
completion(finished);
|
||||
}];
|
||||
} else {
|
||||
CGRect frame = target.frame;
|
||||
int original_y = frame.origin.y;
|
||||
frame.origin.y = self.view.frame.size.height;
|
||||
target.frame = frame;
|
||||
frame.origin.y = original_y;
|
||||
target.hidden = NO;
|
||||
|
||||
[UIView animateWithDuration:0.5
|
||||
delay:0.0
|
||||
options:UIViewAnimationOptionCurveEaseOut
|
||||
animations:^{
|
||||
target.frame = frame;
|
||||
}
|
||||
completion:^(BOOL finished) {
|
||||
target.frame = frame; // in case application did not finish
|
||||
if (completion)
|
||||
completion(finished);
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface CallPausedTableView : UITableViewController
|
||||
|
||||
- (void)update;
|
||||
|
||||
@end
|
||||
|
|
@ -1,104 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#import "CallPausedTableView.h"
|
||||
#import "UICallPausedCell.h"
|
||||
#import "LinphoneManager.h"
|
||||
#import "Utils.h"
|
||||
|
||||
@implementation CallPausedTableView
|
||||
|
||||
#pragma mark - UI change
|
||||
|
||||
- (void)update {
|
||||
[self.tableView reloadData];
|
||||
CGRect newOrigin = self.tableView.frame;
|
||||
newOrigin.size.height =
|
||||
[self tableView:self.tableView heightForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]] *
|
||||
[self tableView:self.tableView numberOfRowsInSection:0];
|
||||
newOrigin.origin.y += self.tableView.frame.size.height - newOrigin.size.height;
|
||||
self.tableView.frame = newOrigin;
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDataSource Functions
|
||||
- (LinphoneCall *)conferenceCallForRow:(NSInteger)row {
|
||||
const MSList *calls = linphone_core_get_calls(LC);
|
||||
int i = -1;
|
||||
while (calls) {
|
||||
if (linphone_call_get_state(calls->data) == LinphoneCallPaused) {
|
||||
i++;
|
||||
if (i == row)
|
||||
break;
|
||||
}
|
||||
calls = calls->next;
|
||||
}
|
||||
// we should reach this only when we are querying for conference
|
||||
return (calls ? calls->data : NULL);
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDataSource Functions
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
NSString *kCellId = NSStringFromClass(UICallPausedCell.class);
|
||||
UICallPausedCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellId];
|
||||
if (cell == nil) {
|
||||
cell = [[UICallPausedCell alloc] initWithIdentifier:kCellId];
|
||||
}
|
||||
[cell setCall:[self conferenceCallForRow:indexPath.row]];
|
||||
cell.contentView.userInteractionEnabled = false;
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
const MSList *calls = linphone_core_get_calls(LC);
|
||||
int count = 0;
|
||||
int conference_in_pause = 0;
|
||||
int call_in_conference = 0;
|
||||
while (calls) {
|
||||
LinphoneCall *call = calls->data;
|
||||
BOOL callInConference = linphone_call_params_get_local_conference_mode(linphone_call_get_current_params(call));
|
||||
if (linphone_call_get_state(call) == LinphoneCallPaused) {
|
||||
count++;
|
||||
}
|
||||
if(callInConference) {
|
||||
call_in_conference++;
|
||||
if (!linphone_core_is_in_conference(LC)) {
|
||||
conference_in_pause = 1;
|
||||
}
|
||||
}
|
||||
calls = calls->next;
|
||||
}
|
||||
if(call_in_conference == 1) {
|
||||
conference_in_pause = 0;
|
||||
}
|
||||
return count + conference_in_pause;
|
||||
}
|
||||
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
|
||||
return 1;
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
|
||||
return 1e-5;
|
||||
}
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
|
||||
return 1e-5;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "SideMenuTableView.h"
|
||||
#import "PhoneMainView.h"
|
||||
|
||||
@interface CallSideMenuView : UIViewController
|
||||
|
||||
@property(weak, nonatomic) IBOutlet UILabel *statsLabel;
|
||||
|
||||
- (IBAction)onLateralSwipe:(id)sender;
|
||||
|
||||
@end
|
||||
|
|
@ -1,230 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#import "CallSideMenuView.h"
|
||||
#import "LinphoneManager.h"
|
||||
#import "PhoneMainView.h"
|
||||
|
||||
@implementation CallSideMenuView {
|
||||
NSTimer *updateTimer;
|
||||
}
|
||||
|
||||
#pragma mark - ViewController Functions
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
if (updateTimer != nil) {
|
||||
[updateTimer invalidate];
|
||||
}
|
||||
updateTimer = [NSTimer scheduledTimerWithTimeInterval:1
|
||||
target:self
|
||||
selector:@selector(updateStats:)
|
||||
userInfo:nil
|
||||
repeats:YES];
|
||||
|
||||
[self updateStats:nil];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
[super viewWillDisappear:animated];
|
||||
if (updateTimer != nil) {
|
||||
[updateTimer invalidate];
|
||||
updateTimer = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)onLateralSwipe:(id)sender {
|
||||
[PhoneMainView.instance.mainViewController hideSideMenu:YES];
|
||||
}
|
||||
|
||||
+ (NSString *)iceToString:(LinphoneIceState)state {
|
||||
switch (state) {
|
||||
case LinphoneIceStateNotActivated:
|
||||
return NSLocalizedString(@"Not activated", @"ICE has not been activated for this call");
|
||||
break;
|
||||
case LinphoneIceStateFailed:
|
||||
return NSLocalizedString(@"Failed", @"ICE processing has failed");
|
||||
break;
|
||||
case LinphoneIceStateInProgress:
|
||||
return NSLocalizedString(@"In progress", @"ICE process is in progress");
|
||||
break;
|
||||
case LinphoneIceStateHostConnection:
|
||||
return NSLocalizedString(@"Direct connection",
|
||||
@"ICE has established a direct connection to the remote host");
|
||||
break;
|
||||
case LinphoneIceStateReflexiveConnection:
|
||||
return NSLocalizedString(
|
||||
@"NAT(s) connection",
|
||||
@"ICE has established a connection to the remote host through one or several NATs");
|
||||
break;
|
||||
case LinphoneIceStateRelayConnection:
|
||||
return NSLocalizedString(@"Relay connection", @"ICE has established a connection through a relay");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSString*)afinetToString:(int)remote_family {
|
||||
return (remote_family == LinphoneAddressFamilyUnspec) ? @"Unspecified":(remote_family == LinphoneAddressFamilyInet) ? @"IPv4" : @"IPv6";
|
||||
}
|
||||
|
||||
+ (NSString *)mediaEncryptionToString:(LinphoneMediaEncryption)enc {
|
||||
switch (enc) {
|
||||
case LinphoneMediaEncryptionDTLS:
|
||||
return @"DTLS";
|
||||
case LinphoneMediaEncryptionSRTP:
|
||||
return @"SRTP";
|
||||
case LinphoneMediaEncryptionZRTP:
|
||||
return @"ZRTP";
|
||||
case LinphoneMediaEncryptionNone:
|
||||
break;
|
||||
}
|
||||
return NSLocalizedString(@"None", nil);
|
||||
}
|
||||
|
||||
- (NSString *)updateStatsForCall:(LinphoneCall *)call stream:(LinphoneStreamType)stream {
|
||||
NSMutableString *result = [[NSMutableString alloc] init];
|
||||
const PayloadType *payload = NULL;
|
||||
const LinphoneCallStats *stats;
|
||||
const LinphoneCallParams *params = linphone_call_get_current_params(call);
|
||||
NSString *name;
|
||||
|
||||
switch (stream) {
|
||||
case LinphoneStreamTypeAudio:
|
||||
name = @"Audio";
|
||||
payload = linphone_call_params_get_used_audio_codec(params);
|
||||
stats = linphone_call_get_audio_stats(call);
|
||||
break;
|
||||
case LinphoneStreamTypeText:
|
||||
name = @"Text";
|
||||
payload = linphone_call_params_get_used_text_codec(params);
|
||||
stats = linphone_call_get_text_stats(call);
|
||||
break;
|
||||
case LinphoneStreamTypeVideo:
|
||||
name = @"Video";
|
||||
payload = linphone_call_params_get_used_video_codec(params);
|
||||
stats = linphone_call_get_video_stats(call);
|
||||
break;
|
||||
case LinphoneStreamTypeUnknown:
|
||||
break;
|
||||
}
|
||||
if (payload == NULL) {
|
||||
return result;
|
||||
}
|
||||
|
||||
[result appendString:@"\n"];
|
||||
[result appendString:name];
|
||||
[result appendString:@"\n"];
|
||||
|
||||
[result appendString:[NSString stringWithFormat:@"Codec: %s/%iHz", payload->mime_type, payload->clock_rate]];
|
||||
if (stream == LinphoneStreamTypeAudio) {
|
||||
[result appendString:[NSString stringWithFormat:@"/%i channels", payload->channels]];
|
||||
}
|
||||
[result appendString:@"\n"];
|
||||
// Encoder & decoder descriptions
|
||||
const char *enc_desc = ms_factory_get_encoder(linphone_core_get_ms_factory(LC), payload->mime_type)->text;
|
||||
const char *dec_desc = ms_factory_get_decoder(linphone_core_get_ms_factory(LC), payload->mime_type)->text;
|
||||
if (strcmp(enc_desc, dec_desc) == 0) {
|
||||
[result appendString:[NSString stringWithFormat:@"Encoder/Decoder: %s", enc_desc]];
|
||||
[result appendString:@"\n"];
|
||||
} else {
|
||||
[result appendString:[NSString stringWithFormat:@"Encoder: %s", enc_desc]];
|
||||
[result appendString:@"\n"];
|
||||
[result appendString:[NSString stringWithFormat:@"Decoder: %s", dec_desc]];
|
||||
[result appendString:@"\n"];
|
||||
}
|
||||
|
||||
if (stats != NULL) {
|
||||
[result appendString:[NSString stringWithFormat:@"Download bandwidth: %1.1f kbits/s",
|
||||
linphone_call_stats_get_download_bandwidth(stats)]];
|
||||
[result appendString:@"\n"];
|
||||
[result appendString:[NSString stringWithFormat:@"Upload bandwidth: %1.1f kbits/s",
|
||||
linphone_call_stats_get_upload_bandwidth(stats)]];
|
||||
[result appendString:@"\n"];
|
||||
if (stream == LinphoneStreamTypeVideo) {
|
||||
/*[result appendString:[NSString stringWithFormat:@"Estimated download bandwidth: %1.1f kbits/s",
|
||||
linphone_call_stats_get_estimated_download_bandwidth(stats)]];
|
||||
[result appendString:@"\n"];*/
|
||||
}
|
||||
[result
|
||||
appendString:[NSString stringWithFormat:@"ICE state: %@",
|
||||
[self.class iceToString:linphone_call_stats_get_ice_state(stats)]]];
|
||||
[result appendString:@"\n"];
|
||||
[result
|
||||
appendString:[NSString
|
||||
stringWithFormat:@"Afinet: %@",
|
||||
[self.class afinetToString:linphone_call_stats_get_ip_family_of_remote(
|
||||
stats)]]];
|
||||
[result appendString:@"\n"];
|
||||
|
||||
// RTP stats section (packet loss count, etc)
|
||||
const rtp_stats_t rtp_stats = *linphone_call_stats_get_rtp_stats(stats);
|
||||
[result
|
||||
appendString:[NSString stringWithFormat:
|
||||
@"RTP packets: %llu total, %lld cum loss, %llu discarded, %llu OOT, %llu bad",
|
||||
rtp_stats.packet_recv, rtp_stats.cum_packet_loss, rtp_stats.discarded,
|
||||
rtp_stats.outoftime, rtp_stats.bad]];
|
||||
[result appendString:@"\n"];
|
||||
[result appendString:[NSString stringWithFormat:@"Sender loss rate: %.2f%%",
|
||||
linphone_call_stats_get_sender_loss_rate(stats)]];
|
||||
[result appendString:@"\n"];
|
||||
[result appendString:[NSString stringWithFormat:@"Receiver loss rate: %.2f%%",
|
||||
linphone_call_stats_get_receiver_loss_rate(stats)]];
|
||||
[result appendString:@"\n"];
|
||||
|
||||
if (stream == LinphoneStreamTypeVideo) {
|
||||
const LinphoneVideoDefinition *recv_definition = linphone_call_params_get_received_video_definition(params);
|
||||
const LinphoneVideoDefinition *sent_definition = linphone_call_params_get_sent_video_definition(params);
|
||||
float sentFPS = linphone_call_params_get_sent_framerate(params);
|
||||
float recvFPS = linphone_call_params_get_received_framerate(params);
|
||||
[result appendString:[NSString stringWithFormat:@"Sent video resolution: %dx%d (%.1fFPS)", linphone_video_definition_get_width(sent_definition),
|
||||
linphone_video_definition_get_height(sent_definition), sentFPS]];
|
||||
[result appendString:@"\n"];
|
||||
[result appendString:[NSString stringWithFormat:@"Received video resolution: %dx%d (%.1fFPS)",
|
||||
linphone_video_definition_get_width(recv_definition),
|
||||
linphone_video_definition_get_height(recv_definition), recvFPS]];
|
||||
[result appendString:@"\n"];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void)updateStats:(NSTimer *)timer {
|
||||
LinphoneCall *call = linphone_core_get_current_call(LC);
|
||||
|
||||
if (!call) {
|
||||
_statsLabel.text = NSLocalizedString(@"No call in progress", nil);
|
||||
return;
|
||||
}
|
||||
|
||||
NSMutableString *stats = [[NSMutableString alloc] init];
|
||||
|
||||
LinphoneMediaEncryption enc = linphone_call_params_get_media_encryption(linphone_call_get_current_params(call));
|
||||
if (enc != LinphoneMediaEncryptionNone) {
|
||||
[stats appendString:[NSString
|
||||
stringWithFormat:@"Call encrypted using %@", [self.class mediaEncryptionToString:enc]]];
|
||||
}
|
||||
|
||||
[stats appendString:[self updateStatsForCall:call stream:LinphoneStreamTypeAudio]];
|
||||
[stats appendString:[self updateStatsForCall:call stream:LinphoneStreamTypeVideo]];
|
||||
[stats appendString:[self updateStatsForCall:call stream:LinphoneStreamTypeText]];
|
||||
|
||||
_statsLabel.text = stats;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="9531" systemVersion="15D21" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="CallSideMenuView">
|
||||
<connections>
|
||||
<outlet property="statsLabel" destination="ZYY-EM-M2s" id="Syl-sZ-upy"/>
|
||||
<outlet property="view" destination="YEG-7O-7jE" id="VGG-cE-thT"/>
|
||||
</connections>
|
||||
</placeholder>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="YEG-7O-7jE">
|
||||
<rect key="frame" x="0.0" y="42" width="375" height="625"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" alpha="0.69999999999999973" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="100" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" id="ZYY-EM-M2s" userLabel="statsLabel">
|
||||
<rect key="frame" x="0.0" y="0.0" width="300" height="625"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<string key="text">Audio: upr
|
||||
Video: down</string>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<gestureRecognizers/>
|
||||
<connections>
|
||||
<outletCollection property="gestureRecognizers" destination="EB5-NY-DqU" appends="YES" id="Bz9-rW-UqV"/>
|
||||
</connections>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<point key="canvasLocation" x="686.23188405797111" y="213.28125"/>
|
||||
</view>
|
||||
<tapGestureRecognizer id="EB5-NY-DqU">
|
||||
<connections>
|
||||
<action selector="onLateralSwipe:" destination="-1" id="Li9-LU-Om1"/>
|
||||
</connections>
|
||||
</tapGestureRecognizer>
|
||||
</objects>
|
||||
</document>
|
||||
|
|
@ -1,117 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "VideoZoomHandler.h"
|
||||
#import "UICamSwitch.h"
|
||||
|
||||
#import "UICompositeView.h"
|
||||
#import "CallPausedTableView.h"
|
||||
|
||||
#import "UIMutedMicroButton.h"
|
||||
#import "UIPauseButton.h"
|
||||
#import "UISpeakerButton.h"
|
||||
#import "UIVideoButton.h"
|
||||
#import "UIHangUpButton.h"
|
||||
#import "UIDigitButton.h"
|
||||
#import "UIRoundedImageView.h"
|
||||
#import "UIBouncingView.h"
|
||||
|
||||
@class VideoView;
|
||||
|
||||
@interface CallView : TPMultiLayoutViewController <UIGestureRecognizerDelegate, UICompositeViewDelegate> {
|
||||
@private
|
||||
UITapGestureRecognizer *singleFingerTap;
|
||||
NSTimer *hideControlsTimer;
|
||||
NSTimer *videoDismissTimer;
|
||||
BOOL videoHidden;
|
||||
BOOL callRecording;
|
||||
VideoZoomHandler *videoZoomHandler;
|
||||
}
|
||||
|
||||
@property(nonatomic, strong) IBOutlet CallPausedTableView *pausedCallsTable;
|
||||
|
||||
@property(nonatomic, strong) IBOutlet UIView *videoGroup;
|
||||
@property(nonatomic, strong) IBOutlet UIView *videoView;
|
||||
@property(nonatomic, strong) IBOutlet UIView *videoPreview;
|
||||
@property(nonatomic, strong) IBOutlet UICamSwitch *videoCameraSwitch;
|
||||
@property(nonatomic, strong) IBOutlet UIActivityIndicatorView *videoWaitingForFirstImage;
|
||||
@property(weak, nonatomic) IBOutlet UIView *callView;
|
||||
|
||||
@property(nonatomic, strong) IBOutlet UIPauseButton *callPauseButton;
|
||||
@property(nonatomic, strong) IBOutlet UIButton *optionsConferenceButton;
|
||||
@property(nonatomic, strong) IBOutlet UIVideoButton *videoButton;
|
||||
@property(nonatomic, strong) IBOutlet UIMutedMicroButton *microButton;
|
||||
@property(nonatomic, strong) IBOutlet UISpeakerButton *speakerButton;
|
||||
@property(nonatomic, strong) IBOutlet UIToggleButton *routesButton;
|
||||
@property(nonatomic, strong) IBOutlet UIToggleButton *optionsButton;
|
||||
@property(nonatomic, strong) IBOutlet UIHangUpButton *hangupButton;
|
||||
@property(nonatomic, strong) IBOutlet UIView *numpadView;
|
||||
@property(nonatomic, strong) IBOutlet UIView *routesView;
|
||||
@property(nonatomic, strong) IBOutlet UIView *optionsView;
|
||||
@property(nonatomic, strong) IBOutlet UIButton *routesEarpieceButton;
|
||||
@property(nonatomic, strong) IBOutlet UIButton *routesSpeakerButton;
|
||||
@property(nonatomic, strong) IBOutlet UIButton *routesBluetoothButton;
|
||||
@property(nonatomic, strong) IBOutlet UIButton *optionsAddButton;
|
||||
@property(nonatomic, strong) IBOutlet UIButton *optionsTransferButton;
|
||||
@property(nonatomic, strong) IBOutlet UIToggleButton *numpadButton;
|
||||
@property(weak, nonatomic) IBOutlet UIPauseButton *conferencePauseButton;
|
||||
@property(weak, nonatomic) IBOutlet UIBouncingView *chatNotificationView;
|
||||
@property(weak, nonatomic) IBOutlet UILabel *chatNotificationLabel;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *recordButton;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *recordButtonOnView;
|
||||
|
||||
@property(weak, nonatomic) IBOutlet UIView *bottomBar;
|
||||
@property(nonatomic, strong) IBOutlet UIDigitButton *oneButton;
|
||||
@property(nonatomic, strong) IBOutlet UIDigitButton *twoButton;
|
||||
@property(nonatomic, strong) IBOutlet UIDigitButton *threeButton;
|
||||
@property(nonatomic, strong) IBOutlet UIDigitButton *fourButton;
|
||||
@property(nonatomic, strong) IBOutlet UIDigitButton *fiveButton;
|
||||
@property(nonatomic, strong) IBOutlet UIDigitButton *sixButton;
|
||||
@property(nonatomic, strong) IBOutlet UIDigitButton *sevenButton;
|
||||
@property(nonatomic, strong) IBOutlet UIDigitButton *eightButton;
|
||||
@property(nonatomic, strong) IBOutlet UIDigitButton *nineButton;
|
||||
@property(nonatomic, strong) IBOutlet UIDigitButton *starButton;
|
||||
@property(nonatomic, strong) IBOutlet UIDigitButton *zeroButton;
|
||||
@property(nonatomic, strong) IBOutlet UIDigitButton *hashButton;
|
||||
@property(weak, nonatomic) IBOutlet UIRoundedImageView *avatarImage;
|
||||
@property(weak, nonatomic) IBOutlet UILabel *nameLabel;
|
||||
@property(weak, nonatomic) IBOutlet UILabel *durationLabel;
|
||||
@property(weak, nonatomic) IBOutlet UIView *pausedByRemoteView;
|
||||
@property(weak, nonatomic) IBOutlet UIView *noActiveCallView;
|
||||
@property(weak, nonatomic) IBOutlet UIView *conferenceView;
|
||||
@property(strong, nonatomic) IBOutlet CallPausedTableView *conferenceCallsTable;
|
||||
@property (weak, nonatomic) IBOutlet UIView *waitView;
|
||||
@property (weak, nonatomic) IBOutlet UIView *infoView;
|
||||
|
||||
- (IBAction)onRoutesClick:(id)sender;
|
||||
- (IBAction)onRoutesBluetoothClick:(id)sender;
|
||||
- (IBAction)onRoutesEarpieceClick:(id)sender;
|
||||
- (IBAction)onRoutesSpeakerClick:(id)sender;
|
||||
- (IBAction)onOptionsClick:(id)sender;
|
||||
- (IBAction)onOptionsTransferClick:(id)sender;
|
||||
- (IBAction)onOptionsAddClick:(id)sender;
|
||||
- (IBAction)onOptionsConferenceClick:(id)sender;
|
||||
- (IBAction)onNumpadClick:(id)sender;
|
||||
- (IBAction)onChatClick:(id)sender;
|
||||
- (IBAction)onRecordClick:(id)sender;
|
||||
- (IBAction)onRecordOnViewClick:(id)sender;
|
||||
|
||||
@end
|
||||
1007
Classes/CallView.m
1007
Classes/CallView.m
File diff suppressed because it is too large
Load diff
|
|
@ -45,6 +45,11 @@
|
|||
@property(nonatomic) Boolean isGroupChat;
|
||||
@property(nonatomic) Boolean isEncrypted;
|
||||
|
||||
@property(nonatomic) Boolean isForVoipConference;
|
||||
@property(nonatomic) Boolean isForOngoingVoipConference;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UILabel *voipTitle;
|
||||
|
||||
- (IBAction)onBackClick:(id)sender;
|
||||
- (IBAction)onNextClick:(id)sender;
|
||||
- (IBAction)onChiffreClick:(id)sender;
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#import "ChatConversationCreateView.h"
|
||||
#import "PhoneMainView.h"
|
||||
#import "UIChatCreateCollectionViewCell.h"
|
||||
#import "linphoneapp-Swift.h"
|
||||
|
||||
@implementation ChatConversationCreateView
|
||||
|
||||
|
|
@ -81,6 +82,22 @@ static UICompositeViewDescription *compositeDescription = nil;
|
|||
frame.origin.x = _linphoneButton.frame.origin.x;
|
||||
_allButton.frame = frame;
|
||||
}
|
||||
|
||||
|
||||
if (_isForVoipConference) {
|
||||
_switchView.hidden = true;
|
||||
_chiffreOptionView.hidden = true;
|
||||
_voipTitle.hidden = false;
|
||||
if (_isForOngoingVoipConference) {
|
||||
[_nextButton setImage:[UIImage imageNamed:@"valid_default"] forState:UIControlStateNormal];
|
||||
} else {
|
||||
[_nextButton setImage:[UIImage imageNamed:@"next_default"] forState:UIControlStateNormal];
|
||||
}
|
||||
} else {
|
||||
_voipTitle.hidden = true;
|
||||
[_nextButton setImage:[UIImage imageNamed:@"next_default"] forState:UIControlStateNormal];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
- (void)viewUpdateEvent:(NSNotification *)notif {
|
||||
|
|
@ -153,11 +170,21 @@ static UICompositeViewDescription *compositeDescription = nil;
|
|||
}
|
||||
|
||||
- (IBAction)onNextClick:(id)sender {
|
||||
ChatConversationInfoView *view = VIEW(ChatConversationInfoView);
|
||||
view.contacts = _tableController.contactsGroup;
|
||||
view.create = !_isForEditing;
|
||||
view.encrypted = _isEncrypted;
|
||||
[PhoneMainView.instance changeCurrentView:view.compositeViewDescription];
|
||||
if (_isForVoipConference) {
|
||||
if (_isForOngoingVoipConference) {
|
||||
[PhoneMainView.instance changeCurrentView:VIEW(ActiveCallOrConferenceView).compositeViewDescription];
|
||||
[ConferenceViewModelBridge updateParticipantsListWithAddresses:_tableController.contactsGroup];
|
||||
} else {
|
||||
[PhoneMainView.instance changeCurrentView:VIEW(ConferenceSchedulingSummaryView).compositeViewDescription];
|
||||
[VIEW(ConferenceSchedulingSummaryView) setParticipantsWithAddresses:_tableController.contactsGroup];
|
||||
}
|
||||
} else {
|
||||
ChatConversationInfoView *view = VIEW(ChatConversationInfoView);
|
||||
view.contacts = _tableController.contactsGroup;
|
||||
view.create = !_isForEditing;
|
||||
view.encrypted = _isEncrypted;
|
||||
[PhoneMainView.instance changeCurrentView:view.compositeViewDescription];
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)onChiffreClick:(id)sender {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#import "UICompositeView.h"
|
||||
#import "UIRoundBorderedButton.h"
|
||||
#import "UIChatBubbleTextCell.h"
|
||||
|
||||
@interface ChatConversationImdnView : UIViewController <UICompositeViewDelegate, UITableViewDelegate, UITableViewDataSource>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#import "UIChatBubblePhotoCell.h"
|
||||
#import "UIChatNotifiedEventCell.h"
|
||||
#import "PhoneMainView.h"
|
||||
#import "linphoneapp-Swift.h"
|
||||
|
||||
@implementation ChatConversationTableView
|
||||
|
||||
|
|
@ -335,7 +336,8 @@ static const int BASIC_EVENT_LIST=15;
|
|||
LinphoneEventLog *event = [[eventList objectAtIndex:indexPath.row] pointerValue];
|
||||
if (linphone_event_log_get_type(event) == LinphoneEventLogTypeConferenceChatMessage) {
|
||||
LinphoneChatMessage *chat = linphone_event_log_get_chat_message(event);
|
||||
if (linphone_chat_message_get_file_transfer_information(chat) || linphone_chat_message_get_external_body_url(chat))
|
||||
BOOL isConferenceIcs = [ICSBubbleView isConferenceInvitationMessageWithCmessage:chat];
|
||||
if (!isConferenceIcs && (linphone_chat_message_get_file_transfer_information(chat) || linphone_chat_message_get_external_body_url(chat)))
|
||||
kCellId = NSStringFromClass(UIChatBubblePhotoCell.class);
|
||||
else
|
||||
kCellId = NSStringFromClass(UIChatBubbleTextCell.class);
|
||||
|
|
@ -382,14 +384,12 @@ static const CGFloat MESSAGE_SPACING_PERCENTAGE = 1.f;
|
|||
LinphoneEventLog *event = [[eventList objectAtIndex:indexPath.row] pointerValue];
|
||||
if (linphone_event_log_get_type(event) == LinphoneEventLogTypeConferenceChatMessage) {
|
||||
LinphoneChatMessage *chat = linphone_event_log_get_chat_message(event);
|
||||
|
||||
//If the message is followed by another one that is not from the same address, we add a little space under it
|
||||
CGFloat height = 0;
|
||||
if ([self isLastIndexInTableView:indexPath chat:chat])
|
||||
height += tableView.frame.size.height * MESSAGE_SPACING_PERCENTAGE / 100;
|
||||
if (![self isFirstIndexInTableView:indexPath chat:chat])
|
||||
height -= 20;
|
||||
|
||||
//If the message is followed by another one that is not from the same address, we add a little space under it
|
||||
CGFloat height = 0;
|
||||
if ([self isLastIndexInTableView:indexPath chat:chat])
|
||||
height += tableView.frame.size.height * MESSAGE_SPACING_PERCENTAGE / 100;
|
||||
if (![self isFirstIndexInTableView:indexPath chat:chat])
|
||||
height -= 20;
|
||||
return [UIChatBubbleTextCell ViewHeightForMessage:chat withWidth:self.view.frame.size.width].height + height;
|
||||
}
|
||||
return [UIChatNotifiedEventCell height];
|
||||
|
|
|
|||
|
|
@ -81,7 +81,6 @@
|
|||
@property(nonatomic, strong) IBOutlet UIButton *pictureButton;
|
||||
@property(weak, nonatomic) IBOutlet UIButton *callButton;
|
||||
@property(weak, nonatomic) IBOutlet UIBackToCallButton *backToCallButton;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *infoButton;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *particpantsLabel;
|
||||
@property NSMutableArray <NSNumber *> *qualitySettingsArray;
|
||||
@property (weak, nonatomic) IBOutlet UICollectionView *imagesCollectionView;
|
||||
|
|
@ -161,4 +160,6 @@
|
|||
|
||||
-(void) initiateReplyViewForMessage:(LinphoneChatMessage *)message;
|
||||
|
||||
-(void) stopVoiceRecording;
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -659,11 +659,32 @@ static UICompositeViewDescription *compositeDescription = nil;
|
|||
}];
|
||||
}
|
||||
|
||||
- (BOOL) groupCallAvailable {
|
||||
if (isOneToOne || !_backToCallButton.hidden || _tableController.tableView.isEditing)
|
||||
return false;
|
||||
LinphoneAccount *account = linphone_core_get_default_account(LC);
|
||||
if (!account)
|
||||
return false;
|
||||
const LinphoneAccountParams *params = linphone_account_get_params(account);
|
||||
if (!params)
|
||||
return false;
|
||||
return linphone_account_params_get_audio_video_conference_factory_address(params) != nil || linphone_account_params_get_conference_factory_uri(params) != nil;
|
||||
|
||||
}
|
||||
|
||||
- (void)updateSuperposedButtons {
|
||||
[_backToCallButton update];
|
||||
_infoButton.hidden = (isOneToOne|| !_backToCallButton.hidden || _tableController.tableView.isEditing);
|
||||
_callButton.hidden = !_backToCallButton.hidden || !_infoButton.hidden || _tableController.tableView.isEditing;
|
||||
_callButton.hidden = !_backToCallButton.hidden || _tableController.tableView.isEditing;
|
||||
_toggleMenuButton.hidden = _tableController.isEditing;
|
||||
|
||||
// Group call :
|
||||
if (self.groupCallAvailable ) {
|
||||
[_callButton setImage: [LinphoneUtils resizeImage:[UIImage imageNamed:@"voip_conference_new"] newSize:CGSizeMake(50, 50)] forState:UIControlStateNormal];
|
||||
_callButton.hidden = false;
|
||||
} else {
|
||||
[_callButton setImage:[UIImage imageNamed:@"call_alt_start_default"] forState:UIControlStateNormal];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
- (void)updateParticipantLabel {
|
||||
|
|
@ -861,7 +882,20 @@ static UICompositeViewDescription *compositeDescription = nil;
|
|||
bctbx_list_t *participants = linphone_chat_room_get_participants(_chatRoom);
|
||||
LinphoneParticipant *firstParticipant = participants ? (LinphoneParticipant *)participants->data : NULL;
|
||||
const LinphoneAddress *addr = firstParticipant ? linphone_participant_get_address(firstParticipant) : linphone_chat_room_get_peer_address(_chatRoom);
|
||||
[LinphoneManager.instance call:addr];
|
||||
if (self.groupCallAvailable) {
|
||||
UIConfirmationDialog *d = [UIConfirmationDialog ShowWithMessage:VoipTexts.conference_start_group_call_dialog_message
|
||||
cancelMessage:nil
|
||||
confirmMessage:VoipTexts.conference_start_group_call_dialog_ok_button
|
||||
onCancelClick:^() {}
|
||||
onConfirmationClick:^() {
|
||||
[ConferenceViewModelBridge startGroupCallWithCChatRoom:_chatRoom];
|
||||
}];
|
||||
d.groupCallImage.hidden = NO;
|
||||
[d.groupCallImage setImageNamed:@"voip_conference_new" tintColor:UIColor.whiteColor];
|
||||
[d setSpecialColor];
|
||||
[d setWhiteCancel];
|
||||
} else
|
||||
[LinphoneManager.instance call:addr];
|
||||
}
|
||||
|
||||
- (IBAction)onListSwipe:(id)sender {
|
||||
|
|
@ -1814,7 +1848,7 @@ void on_chat_room_conference_alert(LinphoneChatRoom *cr, const LinphoneEventLog
|
|||
cell.textLabel.text = NSLocalizedString(@"Go to contact",nil);
|
||||
}
|
||||
} else {
|
||||
cell.imageView.image = [LinphoneUtils resizeImage:[UIImage imageNamed:@"chat_group_informations.png"] newSize:CGSizeMake(20, 25)];
|
||||
cell.imageView.image = [LinphoneUtils resizeImage:[UIImage imageNamed:@"chat_group_informations.png"] newSize:CGSizeMake(25, 25)];
|
||||
cell.textLabel.text = NSLocalizedString(@"Group infos",nil);
|
||||
}
|
||||
}
|
||||
|
|
@ -1847,7 +1881,7 @@ void on_chat_room_conference_alert(LinphoneChatRoom *cr, const LinphoneEventLog
|
|||
|
||||
if ((isEncrypted && ((!canEphemeral && indexPath.row == 4)||(canEphemeral && indexPath.row == 5)))
|
||||
|| (!isEncrypted && indexPath.row == 3)) {
|
||||
cell.imageView.image = [LinphoneUtils resizeImage:[UIImage imageNamed:@"chat_group_informations.png"] newSize:CGSizeMake(20, 25)];
|
||||
cell.imageView.image = [LinphoneUtils resizeImage:[UIImage imageNamed:@"chat_group_informations.png"] newSize:CGSizeMake(25, 25)];
|
||||
cell.textLabel.text = NSLocalizedString(@"Show address and identity",nil);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ static UICompositeViewDescription *compositeDescription = nil;
|
|||
[_tableView reloadData];
|
||||
} else {
|
||||
const LinphoneAddress *addr = linphone_participant_device_get_address(entry->device);
|
||||
[CallManager.instance startCallWithAddr:(LinphoneAddress *)addr isSas:TRUE];
|
||||
[CallManager.instance startCallWithAddr:(LinphoneAddress *)addr isSas:TRUE isVideo:false isConference:false];
|
||||
}
|
||||
} else {
|
||||
bctbx_list_t *devices = linphone_participant_get_devices(entry->participant);
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@
|
|||
|
||||
#import "LinphoneManager.h"
|
||||
#import "PhoneMainView.h"
|
||||
#import "linphoneapp-Swift.h"
|
||||
|
||||
|
||||
@implementation DialerView
|
||||
|
||||
|
|
@ -143,6 +145,9 @@ static UICompositeViewDescription *compositeDescription = nil;
|
|||
[_videoCameraSwitch setHidden:FALSE];
|
||||
}
|
||||
}
|
||||
[_addContactButton setImage:[UIImage imageNamed:@"voip_conference_new"] forState:UIControlStateNormal];
|
||||
_addContactButton.imageView.contentMode = UIViewContentModeScaleAspectFit;
|
||||
_addContactButton.enabled = true;
|
||||
}
|
||||
|
||||
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
|
||||
|
|
@ -170,11 +175,10 @@ static UICompositeViewDescription *compositeDescription = nil;
|
|||
_padView.hidden = !IPAD && UIInterfaceOrientationIsLandscape(toInterfaceOrientation);
|
||||
if (linphone_core_get_calls_nb(LC)) {
|
||||
_backButton.hidden = FALSE;
|
||||
_addContactButton.hidden = TRUE;
|
||||
} else {
|
||||
_backButton.hidden = TRUE;
|
||||
_addContactButton.hidden = FALSE;
|
||||
}
|
||||
_addContactButton.hidden = FALSE;
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
|
|
@ -388,24 +392,19 @@ static UICompositeViewDescription *compositeDescription = nil;
|
|||
#pragma mark - Action Functions
|
||||
|
||||
- (IBAction)onAddContactClick:(id)event {
|
||||
[ContactSelection setSelectionMode:ContactSelectionModeEdit];
|
||||
[ContactSelection setAddAddress:[_addressField text]];
|
||||
[ContactSelection enableSipFilter:FALSE];
|
||||
[PhoneMainView.instance changeCurrentView:ContactsListView.compositeViewDescription];
|
||||
ConferenceSchedulingView *view = VIEW(ConferenceSchedulingView);
|
||||
[view resetViewModel];
|
||||
[PhoneMainView.instance changeCurrentView:ConferenceSchedulingView.compositeViewDescription];
|
||||
}
|
||||
|
||||
- (IBAction)onBackClick:(id)event {
|
||||
[PhoneMainView.instance popToView:CallView.compositeViewDescription];
|
||||
[PhoneMainView.instance popToView:ActiveCallOrConferenceView.compositeViewDescription];
|
||||
}
|
||||
|
||||
- (IBAction)onAddressChange:(id)sender {
|
||||
if ([self displayDebugPopup:_addressField.text]) {
|
||||
_addressField.text = @"";
|
||||
}
|
||||
_addContactButton.enabled = _backspaceButton.enabled = ([[_addressField text] length] > 0);
|
||||
if ([_addressField.text length] == 0) {
|
||||
[self.view endEditing:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)onBackspaceClick:(id)sender {
|
||||
|
|
|
|||
|
|
@ -138,6 +138,7 @@ static UICompositeViewDescription *compositeDescription = nil;
|
|||
_addContactButton.hidden = YES;
|
||||
return;
|
||||
}
|
||||
|
||||
_emptyLabel.hidden = YES;
|
||||
|
||||
const LinphoneAddress *addr = linphone_call_log_get_remote_address(callLog);
|
||||
|
|
|
|||
|
|
@ -25,7 +25,11 @@
|
|||
}
|
||||
|
||||
@property(nonatomic, assign) BOOL missedFilter;
|
||||
@property(nonatomic, assign) BOOL confFilter;
|
||||
|
||||
|
||||
@property(strong, nonatomic) NSMutableDictionary *sections;
|
||||
@property(strong, nonatomic) NSMutableArray *sortedDays;
|
||||
|
||||
- (void)removeFIlters;
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -22,15 +22,18 @@
|
|||
#import "LinphoneManager.h"
|
||||
#import "PhoneMainView.h"
|
||||
#import "Utils.h"
|
||||
#import "linphoneapp-Swift.h"
|
||||
|
||||
|
||||
@implementation HistoryListTableView
|
||||
|
||||
@synthesize missedFilter;
|
||||
@synthesize missedFilter,confFilter;
|
||||
|
||||
#pragma mark - Lifecycle Functions
|
||||
|
||||
- (void)initHistoryTableViewController {
|
||||
missedFilter = false;
|
||||
confFilter = false;
|
||||
}
|
||||
|
||||
- (id)init {
|
||||
|
|
@ -102,9 +105,30 @@
|
|||
return;
|
||||
}
|
||||
missedFilter = amissedFilter;
|
||||
if (missedFilter) {
|
||||
confFilter = false;
|
||||
}
|
||||
[self loadData];
|
||||
}
|
||||
|
||||
- (void)setConfFilter:(BOOL)aconfFilter {
|
||||
if (confFilter == aconfFilter) {
|
||||
return;
|
||||
}
|
||||
confFilter = aconfFilter;
|
||||
if (confFilter) {
|
||||
missedFilter = false;
|
||||
}
|
||||
[self loadData];
|
||||
}
|
||||
|
||||
- (void)removeFIlters {
|
||||
confFilter = false;
|
||||
missedFilter = false;
|
||||
[self loadData];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - UITableViewDataSource Functions
|
||||
|
||||
- (NSDate *)dateAtBeginningOfDayForDate:(NSDate *)inputDate {
|
||||
|
|
@ -129,7 +153,8 @@
|
|||
self.sections = [NSMutableDictionary dictionary];
|
||||
while (logs != NULL) {
|
||||
LinphoneCallLog *log = (LinphoneCallLog *)logs->data;
|
||||
if (!missedFilter || linphone_call_log_get_status(log) == LinphoneCallMissed) {
|
||||
BOOL keepIt = (!missedFilter || linphone_call_log_get_status(log) == LinphoneCallMissed) && (!confFilter||linphone_call_log_was_conference(log)) ;
|
||||
if (keepIt) {
|
||||
NSDate *startDate = [self
|
||||
dateAtBeginningOfDayForDate:[NSDate
|
||||
dateWithTimeIntervalSince1970:linphone_call_log_get_start_date(log)]];
|
||||
|
|
@ -143,7 +168,7 @@
|
|||
|
||||
// if this contact was already the previous entry, do not add it twice
|
||||
LinphoneCallLog *prev = [eventsOnThisDay lastObject] ? [[eventsOnThisDay lastObject] pointerValue] : NULL;
|
||||
if (prev && linphone_address_weak_equal(linphone_call_log_get_remote_address(prev),
|
||||
if (!linphone_call_log_was_conference(log) && prev && linphone_address_weak_equal(linphone_call_log_get_remote_address(prev),
|
||||
linphone_call_log_get_remote_address(log))) {
|
||||
bctbx_list_t *list = linphone_call_log_get_user_data(prev);
|
||||
list = bctbx_list_append(list, linphone_call_log_ref(log));
|
||||
|
|
@ -241,8 +266,15 @@
|
|||
UIHistoryCell *cell = (UIHistoryCell *)[self tableView:tableView cellForRowAtIndexPath:indexPath];
|
||||
[cell onDetails:self];
|
||||
} else {
|
||||
const LinphoneAddress *addr = linphone_call_log_get_remote_address(callLog);
|
||||
[LinphoneManager.instance call:addr];
|
||||
if (linphone_call_log_was_conference(callLog)) {
|
||||
LinphoneConferenceInfo *confInfo = linphone_call_log_get_conference_info(callLog);
|
||||
ConferenceWaitingRoomFragment *view = VIEW(ConferenceWaitingRoomFragment);
|
||||
[view setDetailsWithSubject:[NSString stringWithUTF8String:linphone_conference_info_get_subject(confInfo)] url:[NSString stringWithUTF8String:linphone_address_as_string(linphone_conference_info_get_uri(confInfo))]];
|
||||
[PhoneMainView.instance changeCurrentView:ConferenceWaitingRoomFragment.compositeViewDescription];
|
||||
} else {
|
||||
const LinphoneAddress *addr = linphone_call_log_get_remote_address(callLog);
|
||||
[LinphoneManager.instance call:addr];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
|
||||
@property(nonatomic, strong) IBOutlet UIButton *allButton;
|
||||
@property(nonatomic, strong) IBOutlet UIButton *missedButton;
|
||||
@property (weak, nonatomic) IBOutlet UIInterfaceStyleButton *conferenceButton;
|
||||
@property(weak, nonatomic) IBOutlet UIImageView *selectedButtonImage;
|
||||
@property (weak, nonatomic) IBOutlet UIInterfaceStyleButton *toggleSelectionButton;
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
@implementation HistoryListView
|
||||
|
||||
typedef enum _HistoryView { History_All, History_Missed, History_MAX } HistoryView;
|
||||
typedef enum _HistoryView { History_All, History_Missed, History_Conference, History_MAX } HistoryView;
|
||||
|
||||
#pragma mark - UICompositeViewDelegate Functions
|
||||
|
||||
|
|
@ -48,6 +48,11 @@ static UICompositeViewDescription *compositeDescription = nil;
|
|||
|
||||
#pragma mark - ViewController Functions
|
||||
|
||||
-(void) viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
_conferenceButton.imageView.contentMode = UIViewContentModeScaleAspectFit;
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
|
|
@ -70,18 +75,27 @@ static UICompositeViewDescription *compositeDescription = nil;
|
|||
|
||||
#pragma mark -
|
||||
|
||||
|
||||
- (void)changeView:(HistoryView)view {
|
||||
CGRect frame = _selectedButtonImage.frame;
|
||||
if (view == History_All) {
|
||||
frame.origin.x = _allButton.frame.origin.x;
|
||||
_allButton.selected = TRUE;
|
||||
[_tableController setMissedFilter:FALSE];
|
||||
[_tableController removeFIlters];
|
||||
_missedButton.selected = FALSE;
|
||||
_conferenceButton.selected = false;
|
||||
} else if (view == History_Conference) {
|
||||
frame.origin.x = _conferenceButton.frame.origin.x;
|
||||
_conferenceButton.selected = TRUE;
|
||||
[_tableController setConfFilter:true];
|
||||
_missedButton.selected = FALSE;
|
||||
_allButton.selected = FALSE;
|
||||
} else {
|
||||
frame.origin.x = _missedButton.frame.origin.x;
|
||||
_missedButton.selected = TRUE;
|
||||
[_tableController setMissedFilter:TRUE];
|
||||
_allButton.selected = FALSE;
|
||||
_conferenceButton.selected = false;
|
||||
}
|
||||
_selectedButtonImage.frame = frame;
|
||||
}
|
||||
|
|
@ -96,6 +110,10 @@ static UICompositeViewDescription *compositeDescription = nil;
|
|||
[self changeView:History_Missed];
|
||||
}
|
||||
|
||||
- (IBAction)onConferenceClick:(id)sender {
|
||||
[self changeView:History_Conference];
|
||||
}
|
||||
|
||||
- (IBAction)onDeleteClick:(id)event {
|
||||
NSString *msg = [NSString stringWithFormat:NSLocalizedString(@"Do you want to delete selected logs?", nil)];
|
||||
[UIConfirmationDialog ShowWithMessage:msg
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#import "linphoneapp-Swift.h"
|
||||
#import "LinphoneAppDelegate.h"
|
||||
#import "ContactDetailsView.h"
|
||||
#import "ContactsListView.h"
|
||||
|
|
@ -138,7 +137,7 @@
|
|||
if ((floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max)) {
|
||||
if ([LinphoneManager.instance lpConfigBoolForKey:@"autoanswer_notif_preference"]) {
|
||||
linphone_call_accept(call);
|
||||
[PhoneMainView.instance changeCurrentView:CallView.compositeViewDescription];
|
||||
[PhoneMainView.instance changeCurrentView:ActiveCallOrConferenceView.compositeViewDescription];
|
||||
} else {
|
||||
[PhoneMainView.instance displayIncomingCall:call];
|
||||
}
|
||||
|
|
@ -332,6 +331,8 @@
|
|||
return NO;
|
||||
}
|
||||
|
||||
[PhoneMainView.instance.mainViewController getCachedController:ActiveCallOrConferenceView.compositeViewDescription.name]; // This will create the single instance of the ActiveCallOrConferenceView including listeneres
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
|
@ -422,13 +423,6 @@
|
|||
// used for callkit. Called when active video.
|
||||
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler
|
||||
{
|
||||
|
||||
|
||||
if ([userActivity.activityType isEqualToString:@"INStartVideoCallIntent"]) {
|
||||
LOGI(@"CallKit: satrt video.");
|
||||
CallView *view = VIEW(CallView);
|
||||
[view.videoButton setOn];
|
||||
}
|
||||
if ([userActivity.activityType isEqualToString:@"INStartAudioCallIntent"]) { // tel URI handler.
|
||||
INInteraction *interaction = userActivity.interaction;
|
||||
INStartAudioCallIntent *startAudioCallIntent = (INStartAudioCallIntent *)interaction.intent;
|
||||
|
|
@ -552,7 +546,7 @@
|
|||
|
||||
if ([response.actionIdentifier isEqual:@"Answer"]) {
|
||||
// use the standard handler
|
||||
[PhoneMainView.instance changeCurrentView:CallView.compositeViewDescription];
|
||||
[CallManager.instance acceptCallWithCall:call hasVideo:NO];
|
||||
linphone_call_accept(call);
|
||||
} else if ([response.actionIdentifier isEqual:@"Decline"]) {
|
||||
linphone_call_decline(call, LinphoneReasonDeclined);
|
||||
|
|
@ -590,7 +584,6 @@
|
|||
return;
|
||||
|
||||
[[UNUserNotificationCenter currentNotificationCenter] removeAllDeliveredNotifications];
|
||||
[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)
|
||||
|
|
@ -623,7 +616,7 @@
|
|||
}
|
||||
} else if ([response.notification.request.content.categoryIdentifier isEqual:@"video_request"]) {
|
||||
if (!call) return;
|
||||
[PhoneMainView.instance changeCurrentView:CallView.compositeViewDescription];
|
||||
[PhoneMainView.instance changeCurrentView:ActiveCallOrConferenceView.compositeViewDescription];
|
||||
NSTimer *videoDismissTimer = nil;
|
||||
UIConfirmationDialog *sheet = [UIConfirmationDialog ShowWithMessage:response.notification.request.content.body
|
||||
cancelMessage:nil
|
||||
|
|
@ -707,8 +700,7 @@
|
|||
if ([notification.category isEqualToString:@"incoming_call"]) {
|
||||
if ([identifier isEqualToString:@"answer"]) {
|
||||
// use the standard handler
|
||||
[PhoneMainView.instance changeCurrentView:CallView.compositeViewDescription];
|
||||
linphone_call_accept(call);
|
||||
[CallManager.instance acceptCallWithCall:call hasVideo:NO];
|
||||
} else if ([identifier isEqualToString:@"decline"]) {
|
||||
LinphoneCall *call = linphone_core_get_current_call(LC);
|
||||
if (call)
|
||||
|
|
@ -745,8 +737,7 @@
|
|||
if ([notification.category isEqualToString:@"incoming_call"]) {
|
||||
if ([identifier isEqualToString:@"answer"]) {
|
||||
// use the standard handler
|
||||
[PhoneMainView.instance changeCurrentView:CallView.compositeViewDescription];
|
||||
linphone_call_accept(call);
|
||||
[CallManager.instance acceptCallWithCall:call hasVideo:NO];
|
||||
} else if ([identifier isEqualToString:@"decline"]) {
|
||||
LinphoneCall *call = linphone_core_get_current_call(LC);
|
||||
if (call)
|
||||
|
|
|
|||
|
|
@ -407,6 +407,8 @@
|
|||
{
|
||||
|
||||
[self setBool:[lm lpConfigBoolForKey:@"use_device_ringtone"] forKey:@"use_device_ringtone"];
|
||||
[self setBool:linphone_core_is_record_aware_enabled(LC) forKey:@"record_aware"];
|
||||
|
||||
[self setBool:linphone_core_get_use_info_for_dtmf(LC) forKey:@"sipinfo_dtmf_preference"];
|
||||
[self setBool:linphone_core_get_use_rfc2833_for_dtmf(LC) forKey:@"rfc_dtmf_preference"];
|
||||
|
||||
|
|
@ -931,7 +933,9 @@
|
|||
linphone_core_set_use_rfc2833_for_dtmf(LC, [self boolForKey:@"rfc_dtmf_preference"]);
|
||||
[lm lpConfigSetBool:[self boolForKey:@"use_device_ringtone"] forKey:@"use_device_ringtone"];
|
||||
[ProviderDelegate resetSharedProviderConfiguration];
|
||||
|
||||
|
||||
linphone_core_set_record_aware_enabled(LC, [self boolForKey:@"record_aware"]);
|
||||
|
||||
linphone_core_set_use_info_for_dtmf(LC, [self boolForKey:@"sipinfo_dtmf_preference"]);
|
||||
linphone_core_set_inc_timeout(LC, [self integerForKey:@"incoming_call_timeout_preference"]);
|
||||
linphone_core_set_in_call_timeout(LC, [self integerForKey:@"in_call_timeout_preference"]);
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@
|
|||
#import "LinphoneCoreSettingsStore.h"
|
||||
#import "LinphoneAppDelegate.h"
|
||||
#import "LinphoneManager.h"
|
||||
#import "Utils/AudioHelper.h"
|
||||
#import "Utils/FileTransferDelegate.h"
|
||||
|
||||
#include "linphone/factory.h"
|
||||
|
|
@ -482,6 +481,20 @@ static int check_should_migrate_images(void *data, int argc, char **argv, char *
|
|||
linphone_account_set_params(account, newAccountParams);
|
||||
}
|
||||
}
|
||||
if (!linphone_account_params_get_audio_video_conference_factory_address(newAccountParams) && strcmp(appDomain.UTF8String, linphone_account_params_get_domain(newAccountParams)) == 0) {
|
||||
NSString *uri = [self lpConfigStringForKey:@"default_audio_video_conference_factory_uri" withDefault:@"sip:videoconference-factory2@sip.linphone.org"];
|
||||
LinphoneAddress *a = linphone_factory_create_address(linphone_factory_get(), uri.UTF8String);
|
||||
if (a) {
|
||||
linphone_account_params_set_audio_video_conference_factory_address(newAccountParams, a);
|
||||
linphone_account_set_params(account, newAccountParams);
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp(appDomain.UTF8String, linphone_account_params_get_domain(newAccountParams)) == 0 && !linphone_account_params_rtp_bundle_enabled(newAccountParams)) {
|
||||
linphone_account_params_enable_rtp_bundle(newAccountParams, true);
|
||||
linphone_account_set_params(account,newAccountParams);
|
||||
}
|
||||
|
||||
linphone_account_params_unref(newAccountParams);
|
||||
accounts = accounts->next;
|
||||
}
|
||||
|
|
@ -848,7 +861,7 @@ static void linphone_iphone_popup_password_request(LinphoneCore *lc, LinphoneAut
|
|||
if ((linphone_core_get_max_size_for_auto_download_incoming_files(LC) > -1) && linphone_chat_message_get_file_transfer_information(msg))
|
||||
hasFile = TRUE;
|
||||
|
||||
if (!linphone_chat_message_is_file_transfer(msg) && !linphone_chat_message_is_text(msg) && !hasFile)
|
||||
if (!linphone_chat_message_is_file_transfer(msg) && !linphone_chat_message_is_text(msg) && !hasFile && ![ICSBubbleView isConferenceInvitationMessageWithCmessage:msg])
|
||||
return;
|
||||
|
||||
if (hasFile) {
|
||||
|
|
@ -1183,6 +1196,8 @@ static void linphone_iphone_is_composing_received(LinphoneCore *lc, LinphoneChat
|
|||
[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneCoreUpdate
|
||||
object:LinphoneManager.instance
|
||||
userInfo:dict];
|
||||
|
||||
|
||||
}
|
||||
|
||||
static BOOL libStarted = FALSE;
|
||||
|
|
@ -1869,7 +1884,7 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) {
|
|||
}
|
||||
[self checkLocalNetworkPermission];
|
||||
// For OutgoingCall, show CallOutgoingView
|
||||
[CallManager.instance startCallWithAddr:iaddr isSas:FALSE];
|
||||
[CallManager.instance startCallWithAddr:iaddr isSas:FALSE isVideo:false isConference:false];
|
||||
}
|
||||
|
||||
#pragma mark - Misc Functions
|
||||
|
|
|
|||
|
|
@ -1,76 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18122" 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="18093"/>
|
||||
<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="UICallConferenceCell">
|
||||
<connections>
|
||||
<outlet property="avatarImage" destination="PjC-yS-i03" id="srY-K7-Ajk"/>
|
||||
<outlet property="durationLabel" destination="Jgc-Z9-ItD" id="EGs-SW-dRc"/>
|
||||
<outlet property="kickButton" destination="nOf-6W-BeC" id="NR8-NM-f5V"/>
|
||||
<outlet property="nameLabel" destination="63x-tV-T6U" id="rBP-rS-gbi"/>
|
||||
</connections>
|
||||
</placeholder>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="Sae-wc-2Qz">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="60"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" fixedFrame="YES" image="avatar.png" translatesAutoresizingMaskIntoConstraints="NO" id="PjC-yS-i03" userLabel="avatarImage" customClass="UIRoundedImageView">
|
||||
<rect key="frame" x="8" y="4" width="45" height="54"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Contact avatar">
|
||||
<accessibilityTraits key="traits" image="YES" notEnabled="YES"/>
|
||||
<bool key="isElement" value="YES"/>
|
||||
</accessibility>
|
||||
</imageView>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" fixedFrame="YES" text="00:02:15" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="Jgc-Z9-ItD" userLabel="durationLabel">
|
||||
<rect key="frame" x="61" y="33" width="253" height="27"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Contact name"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||
<color key="textColor" red="0.98766469955444336" green="0.27512490749359131" blue="0.029739789664745331" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" fixedFrame="YES" text="John Doe" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="63x-tV-T6U" userLabel="nameLabel">
|
||||
<rect key="frame" x="61" y="0.0" width="176" height="35"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Contact name"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="24"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" tag="24" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="nOf-6W-BeC" userLabel="kickButton" customClass="UIInterfaceStyleButton">
|
||||
<rect key="frame" x="322" y="17" width="34" height="28"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Pause"/>
|
||||
<state key="normal" image="conference_exit_default.png">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="selected" image="conference_exit_over.png"/>
|
||||
<state key="highlighted" image="conference_exit_over.png"/>
|
||||
<connections>
|
||||
<action selector="onKickClick:" destination="-1" eventType="touchUpInside" id="AG0-aA-dG6"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<nil key="simulatedStatusBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<point key="canvasLocation" x="490.57971014492756" y="28.794642857142854"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="avatar.png" width="414.39999389648438" height="414.39999389648438"/>
|
||||
<image name="conference_exit_default.png" width="55.200000762939453" height="46.400001525878906"/>
|
||||
<image name="conference_exit_over.png" width="55.200000762939453" height="46.400001525878906"/>
|
||||
<systemColor name="systemBackgroundColor">
|
||||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</systemColor>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="9060" systemVersion="15B42" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9051"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="UICallPausedCell">
|
||||
<connections>
|
||||
<outlet property="avatarImage" destination="NBJ-w0-Uvw" id="lGx-U6-4tn"/>
|
||||
<outlet property="durationLabel" destination="tZi-KI-viq" id="TLb-yi-TBY"/>
|
||||
<outlet property="nameLabel" destination="g3t-eS-m7B" id="dKD-7X-u2j"/>
|
||||
<outlet property="pauseButton" destination="Izu-Zj-sHi" id="BEq-dn-lx2"/>
|
||||
</connections>
|
||||
</placeholder>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="Egc-Di-26M">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="50"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" alpha="0.5" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="color_A.png" id="S7U-QZ-e0e" userLabel="backgroundColor">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="50"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<animations/>
|
||||
</imageView>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" image="avatar.png" id="NBJ-w0-Uvw" userLabel="avatarImage" customClass="UIRoundedImageView">
|
||||
<rect key="frame" x="8" y="3" width="45" height="45"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||
<animations/>
|
||||
<accessibility key="accessibilityConfiguration" label="Contact avatar">
|
||||
<accessibilityTraits key="traits" image="YES" notEnabled="YES"/>
|
||||
<bool key="isElement" value="YES"/>
|
||||
</accessibility>
|
||||
</imageView>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="John Doe" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" id="g3t-eS-m7B" userLabel="nameLabel">
|
||||
<rect key="frame" x="61" y="6" width="176" height="37"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<animations/>
|
||||
<accessibility key="accessibilityConfiguration" label="Contact name"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" tag="24" contentMode="scaleToFill" selected="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="Izu-Zj-sHi" userLabel="pauseButton" customClass="UIPauseButton">
|
||||
<rect key="frame" x="322" y="3" width="45" height="45"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
|
||||
<animations/>
|
||||
<accessibility key="accessibilityConfiguration" label="Pause"/>
|
||||
<state key="normal" image="pause_small_default.png">
|
||||
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
<state key="disabled" image="pause_small_disabled.png"/>
|
||||
<state key="selected" image="pause_small_over_selected.png"/>
|
||||
<state key="highlighted" image="pause_small_over_selected.png"/>
|
||||
</button>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="00:02:15" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" id="tZi-KI-viq" userLabel="durationLabel">
|
||||
<rect key="frame" x="245" y="7" width="69" height="37"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<animations/>
|
||||
<accessibility key="accessibilityConfiguration" label="Contact name"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<animations/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="simulatedStatusBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<point key="canvasLocation" x="338.5" y="43"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="avatar.png" width="255" height="255"/>
|
||||
<image name="color_A.png" width="2" height="2"/>
|
||||
<image name="pause_small_default.png" width="34" height="34"/>
|
||||
<image name="pause_small_disabled.png" width="34" height="34"/>
|
||||
<image name="pause_small_over_selected.png" width="34" height="34"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" 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="18093"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
<outlet property="cancelButton" destination="B1K-CB-3of" id="KKi-Xc-ldA"/>
|
||||
<outlet property="confirmationButton" destination="SbQ-re-fGQ" id="yiv-a9-o8E"/>
|
||||
<outlet property="forwardImage" destination="1Wh-Yi-cUe" id="YQq-bt-pk1"/>
|
||||
<outlet property="groupCallImage" destination="SVn-4k-9yc" id="sAP-8V-ttn"/>
|
||||
<outlet property="securityImage" destination="bbo-g3-bGy" id="qZa-li-yrl"/>
|
||||
<outlet property="titleLabel" destination="jLz-g1-cTe" id="qaj-OB-2r1"/>
|
||||
<outlet property="view" destination="2Vb-Xy-rci" id="nNw-EJ-AY3"/>
|
||||
|
|
@ -93,6 +94,10 @@
|
|||
<rect key="frame" x="89" y="50" width="138" height="54"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
</imageView>
|
||||
<imageView hidden="YES" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="voip_conference_new.png" translatesAutoresizingMaskIntoConstraints="NO" id="SVn-4k-9yc">
|
||||
<rect key="frame" x="89" y="50" width="138" height="54"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
</imageView>
|
||||
</subviews>
|
||||
</view>
|
||||
</subviews>
|
||||
|
|
@ -111,5 +116,6 @@
|
|||
<image name="color_M.png" width="2" height="2"/>
|
||||
<image name="forward_message_default.png" width="187" height="148"/>
|
||||
<image name="security_2_indicator.png" width="27.5" height="32.5"/>
|
||||
<image name="voip_conference_new.png" width="97.599998474121094" height="97.599998474121094"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#import "TabBarView.h"
|
||||
#import "PhoneMainView.h"
|
||||
#import "linphoneapp-Swift.h"
|
||||
|
||||
@implementation TabBarView
|
||||
|
||||
|
|
@ -99,7 +100,8 @@
|
|||
|
||||
- (void)updateSelectedButton:(UICompositeViewDescription *)view {
|
||||
_historyButton.selected = [view equal:HistoryListView.compositeViewDescription] ||
|
||||
[view equal:HistoryDetailsView.compositeViewDescription];
|
||||
[view equal:HistoryDetailsView.compositeViewDescription] ||
|
||||
[view equal:ConferenceHistoryDetailsView.compositeViewDescription];
|
||||
_contactsButton.selected = [view equal:ContactsListView.compositeViewDescription] ||
|
||||
[view equal:ContactDetailsView.compositeViewDescription];
|
||||
_dialerButton.selected = [view equal:DialerView.compositeViewDescription];
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@
|
|||
#import "UIBackToCallButton.h"
|
||||
#import "LinphoneManager.h"
|
||||
#import "PhoneMainView.h"
|
||||
#import "linphoneapp-Swift.h"
|
||||
|
||||
|
||||
@implementation UIBackToCallButton
|
||||
|
||||
|
|
@ -46,7 +48,7 @@
|
|||
}
|
||||
|
||||
- (IBAction)onBackToCallClick:(id)sender {
|
||||
[PhoneMainView.instance popToView:CallView.compositeViewDescription];
|
||||
[PhoneMainView.instance popToView:ActiveCallOrConferenceView.compositeViewDescription];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#import "UIRoundedImageView.h"
|
||||
#import "LinphoneManager.h"
|
||||
#import "UIInterfaceStyleButton.h"
|
||||
|
||||
#define CONFERENCE_CELL_HEIGHT 60
|
||||
|
||||
@interface UICallConferenceCell : UITableViewCell
|
||||
|
||||
@property(weak, nonatomic) IBOutlet UIRoundedImageView *avatarImage;
|
||||
@property(weak, nonatomic) IBOutlet UILabel *nameLabel;
|
||||
@property(weak, nonatomic) IBOutlet UILabel *durationLabel;
|
||||
@property (weak, nonatomic) IBOutlet UIInterfaceStyleButton *kickButton;
|
||||
@property(nonatomic, setter=setParticipant:) LinphoneParticipant *participant;
|
||||
|
||||
- (id)initWithIdentifier:(NSString *)identifier;
|
||||
- (IBAction)onKickClick:(id)sender;
|
||||
|
||||
@end
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#import "linphoneapp-Swift.h"
|
||||
#import "UICallConferenceCell.h"
|
||||
#import "Utils.h"
|
||||
#import "PhoneMainView.h"
|
||||
|
||||
@implementation UICallConferenceCell
|
||||
|
||||
- (id)initWithIdentifier:(NSString *)identifier {
|
||||
self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
|
||||
if (self != nil) {
|
||||
NSArray *arrayOfViews =
|
||||
[[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self.class) owner:self options:nil];
|
||||
if ([arrayOfViews count] >= 1) {
|
||||
// resize cell to match .nib size. It is needed when resized the cell to
|
||||
// correctly adapt its height too
|
||||
UIView *sub = ((UIView *)[arrayOfViews objectAtIndex:0]);
|
||||
[self setFrame:CGRectMake(0, 0, sub.frame.size.width, sub.frame.size.height)];
|
||||
[self addSubview:sub];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setParticipant:(LinphoneParticipant *)p {
|
||||
_participant = p;
|
||||
if (!p) {
|
||||
return;
|
||||
}
|
||||
const LinphoneAddress *addr = linphone_participant_get_address(p);
|
||||
[ContactDisplay setDisplayNameLabel:_nameLabel forAddress:addr];
|
||||
_durationLabel.text = [LinphoneUtils durationToString:[NSDate date].timeIntervalSince1970 - linphone_participant_get_creation_time(p)];
|
||||
_kickButton.hidden = CallManager.instance.isInConferenceAsGuest;
|
||||
}
|
||||
|
||||
|
||||
- (IBAction)onKickClick:(id)sender {
|
||||
if (!_participant) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ([CallManager callKitEnabled]) {
|
||||
LinphoneCall *call = [CallManager.instance getCallForParticipant:_participant];
|
||||
if (call) {
|
||||
[CallManager.instance setHeldWithCall:call hold:true];
|
||||
}
|
||||
}
|
||||
linphone_conference_remove_participant_2([CallManager.instance getConference], _participant);
|
||||
|
||||
|
||||
}
|
||||
@end
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#import "UIRoundedImageView.h"
|
||||
#import "LinphoneManager.h"
|
||||
#import "UIPauseButton.h"
|
||||
|
||||
@interface UICallPausedCell : UITableViewCell
|
||||
|
||||
@property(weak, nonatomic) IBOutlet UIRoundedImageView *avatarImage;
|
||||
@property(weak, nonatomic) IBOutlet UILabel *nameLabel;
|
||||
@property(weak, nonatomic) IBOutlet UILabel *durationLabel;
|
||||
@property(weak, nonatomic) IBOutlet UIPauseButton *pauseButton;
|
||||
|
||||
- (id)initWithIdentifier:(NSString *)identifier;
|
||||
- (void)setCall:(LinphoneCall *)call;
|
||||
|
||||
@end
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#import "UICallPausedCell.h"
|
||||
#import "Utils.h"
|
||||
|
||||
@implementation UICallPausedCell
|
||||
|
||||
- (id)initWithIdentifier:(NSString *)identifier {
|
||||
self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
|
||||
if (self != nil) {
|
||||
NSArray *arrayOfViews =
|
||||
[[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self.class) owner:self options:nil];
|
||||
if ([arrayOfViews count] >= 1) {
|
||||
// resize cell to match .nib size. It is needed when resized the cell to
|
||||
// correctly adapt its height too
|
||||
UIView *sub = ((UIView *)[arrayOfViews objectAtIndex:0]);
|
||||
[self setFrame:CGRectMake(0, 0, sub.frame.size.width, sub.frame.size.height)];
|
||||
[self addSubview:sub];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setCall:(LinphoneCall *)call {
|
||||
// if no call is provided, we assume that this is a conference
|
||||
if (!call || linphone_call_get_conference(call)) {
|
||||
[_pauseButton setType:UIPauseButtonType_Conference call:call];
|
||||
_nameLabel.text = NSLocalizedString(@"Conference", nil);
|
||||
[_avatarImage setImage:[UIImage imageNamed:@"options_start_conference_default.png"]
|
||||
bordered:NO
|
||||
withRoundedRadius:YES];
|
||||
_durationLabel.text = @"";
|
||||
} else {
|
||||
[_pauseButton setType:UIPauseButtonType_Call call:call];
|
||||
const LinphoneAddress *addr = linphone_call_get_remote_address(call);
|
||||
[ContactDisplay setDisplayNameLabel:_nameLabel forAddress:addr];
|
||||
[_avatarImage setImage:[FastAddressBook imageForAddress:addr] bordered:NO withRoundedRadius:YES];
|
||||
_durationLabel.text = [LinphoneUtils durationToString:linphone_call_get_duration(call)];
|
||||
}
|
||||
[_pauseButton update];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -24,5 +24,6 @@
|
|||
@interface UICamSwitch : UIIconButton
|
||||
|
||||
@property(nonatomic, weak) IBOutlet UIView *preview;
|
||||
+ (void) switchCamera;
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -34,6 +34,10 @@ INIT_WITH_COMMON_CF {
|
|||
#pragma mark -
|
||||
|
||||
- (void)touchUp:(id)sender {
|
||||
[UICamSwitch switchCamera];
|
||||
}
|
||||
|
||||
+ (void) switchCamera {
|
||||
const char *currentCamId = (char *)linphone_core_get_video_device(LC);
|
||||
const char **cameras = linphone_core_get_video_devices(LC);
|
||||
const char *newCamId = NULL;
|
||||
|
|
@ -50,10 +54,6 @@ INIT_WITH_COMMON_CF {
|
|||
if (newCamId) {
|
||||
LOGI(@"Switching from [%s] to [%s]", currentCamId, newCamId);
|
||||
linphone_core_set_video_device(LC, newCamId);
|
||||
LinphoneCall *call = linphone_core_get_current_call(LC);
|
||||
if (call != NULL) {
|
||||
linphone_call_update(call, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@
|
|||
#define IMAGE_DEFAULT_MARGIN 5
|
||||
#define VOICE_RECORDING_PLAYER_HEIGHT 60
|
||||
#define VOICE_RECORDING_PLAYER_WIDTH 300
|
||||
#define CONFERENCE_INVITATION_HEIGHT 210
|
||||
#define CONFERENCE_INVITATION_WIDTH 300
|
||||
|
||||
|
||||
|
||||
@interface UIChatBubbleTextCell : UITableViewCell <UIDocumentPickerDelegate, UITableViewDataSource,UITableViewDelegate>
|
||||
|
|
@ -66,6 +69,7 @@
|
|||
@property (weak, nonatomic) IBOutlet UIImageView *replyTransferIcon;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *replyTransferLabel;
|
||||
@property (weak, nonatomic) IBOutlet UIView *photoCellContentView;
|
||||
@property UIView *icsBubbleView;
|
||||
|
||||
|
||||
@property(nonatomic) BOOL isFirst;
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@
|
|||
|
||||
@implementation UIChatBubbleTextCell
|
||||
|
||||
|
||||
|
||||
#pragma mark - Lifecycle Functions
|
||||
|
||||
|
||||
|
|
@ -43,6 +45,11 @@
|
|||
UIView *sub = ((UIView *)[arrayOfViews objectAtIndex:arrayOfViews.count - 1]);
|
||||
[self setFrame:CGRectMake(0, 0, sub.frame.size.width, sub.frame.size.height)];
|
||||
[self addSubview:sub];
|
||||
self.icsBubbleView = [[ICSBubbleView alloc] init];
|
||||
self.icsBubbleView.frame = CGRectMake(_messageText.frame.origin.x, _messageText.frame.origin.y+25, CONFERENCE_INVITATION_WIDTH-80, CONFERENCE_INVITATION_HEIGHT-20);
|
||||
[self.innerView addSubview:self.icsBubbleView];
|
||||
[(ICSBubbleView*)self.icsBubbleView setLayoutConstraintsWithView:self.backgroundColorImage];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -275,6 +282,18 @@
|
|||
_replyView.view.hidden = true;
|
||||
}
|
||||
|
||||
// ICS for conference invitations
|
||||
|
||||
if ([ICSBubbleView isConferenceInvitationMessageWithCmessage:self.message]) {
|
||||
[(ICSBubbleView*)self.icsBubbleView setFromChatMessageWithCmessage:self.message];
|
||||
self.icsBubbleView.hidden = false;
|
||||
_messageText.hidden = true;
|
||||
} else {
|
||||
self.icsBubbleView.hidden = true;
|
||||
_messageText.hidden = false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
- (void)setEditing:(BOOL)editing {
|
||||
|
|
@ -470,6 +489,11 @@ static const CGFloat REPLY_OR_FORWARD_TAG_HEIGHT = 18;
|
|||
}
|
||||
|
||||
+ (CGSize)ViewHeightForMessageText:(LinphoneChatMessage *)chat withWidth:(int)width textForImdn:(NSString *)imdnText {
|
||||
|
||||
if ([ICSBubbleView isConferenceInvitationMessageWithCmessage:chat]) {
|
||||
return CGSizeMake(CONFERENCE_INVITATION_WIDTH, CONFERENCE_INVITATION_HEIGHT);
|
||||
}
|
||||
|
||||
NSString *messageText = [UIChatBubbleTextCell TextMessageForChat:chat];
|
||||
static UIFont *messageFont = nil;
|
||||
|
||||
|
|
|
|||
|
|
@ -86,4 +86,5 @@
|
|||
- (void)clearCache:(NSArray *)exclude;
|
||||
- (IBAction)onRightSwipe:(id)sender;
|
||||
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#import "LinphoneAppDelegate.h"
|
||||
#import "Utils.h"
|
||||
#import "SideMenuView.h"
|
||||
#import "linphoneapp-Swift.h"
|
||||
|
||||
@implementation UICompositeViewDescription
|
||||
|
||||
|
|
@ -304,12 +305,15 @@
|
|||
return nil;
|
||||
}
|
||||
|
||||
|
||||
- (void)clearCache:(NSArray *)exclude {
|
||||
|
||||
|
||||
for (NSString *key in [viewControllerCache allKeys]) {
|
||||
bool remove = true;
|
||||
|
||||
/*ImagePickerView can be used as popover and we do NOT want to free it*/;
|
||||
if ([key isEqualToString:ImagePickerView.compositeViewDescription.name]) {
|
||||
if ([key isEqualToString:ImagePickerView.compositeViewDescription.name] || [key isEqualToString:ActiveCallOrConferenceView.compositeViewDescription.name]) {
|
||||
remove = false;
|
||||
} else if (exclude != nil) {
|
||||
for (UICompositeViewDescription *description in exclude) {
|
||||
|
|
|
|||
|
|
@ -46,12 +46,14 @@ typedef void (^UIConfirmationBlock)(void);
|
|||
@property(weak, nonatomic) IBOutlet UIRoundBorderedButton *cancelButton;
|
||||
@property (weak, nonatomic) IBOutlet UIImageView *securityImage;
|
||||
@property (weak, nonatomic) IBOutlet UIImageView *forwardImage;
|
||||
@property (weak, nonatomic) IBOutlet UIImageView *groupCallImage;
|
||||
@property(weak, nonatomic) IBOutlet UIRoundBorderedButton *confirmationButton;
|
||||
@property (weak, nonatomic) IBOutlet UIView *authView;
|
||||
@property(weak, nonatomic) IBOutlet UILabel *titleLabel;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *authButton;
|
||||
|
||||
- (void)setSpecialColor;
|
||||
-(void) setWhiteCancel;
|
||||
- (IBAction)onCancelClick:(id)sender;
|
||||
- (IBAction)onConfirmationClick:(id)sender;
|
||||
- (IBAction)onAuthClick:(id)sender;
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#import "UIConfirmationDialog.h"
|
||||
#import "PhoneMainView.h"
|
||||
#import "linphoneapp-Swift.h""
|
||||
|
||||
@implementation UIConfirmationDialog
|
||||
+ (UIConfirmationDialog *)initDialog:(NSString *)cancel
|
||||
|
|
@ -97,6 +98,13 @@
|
|||
[[UIColor colorWithPatternImage:[UIImage imageNamed:@"color_A.png"]] CGColor];
|
||||
}
|
||||
|
||||
-(void) setWhiteCancel {
|
||||
[_cancelButton setBackgroundImage:nil forState:UIControlStateNormal];
|
||||
[_cancelButton setBackgroundColor:UIColor.whiteColor];
|
||||
[_cancelButton setTitleColor:VoipTheme.voip_dark_gray forState:UIControlStateNormal];
|
||||
_cancelButton.layer.borderColor = UIColor.whiteColor.CGColor;
|
||||
}
|
||||
|
||||
- (IBAction)onCancelClick:(id)sender {
|
||||
[self.view removeFromSuperview];
|
||||
[self removeFromParentViewController];
|
||||
|
|
|
|||
|
|
@ -1,112 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#import "linphoneapp-Swift.h"
|
||||
#import "UIHangUpButton.h"
|
||||
#import "LinphoneManager.h"
|
||||
|
||||
#import "linphoneapp-Swift.h"
|
||||
|
||||
@implementation UIHangUpButton
|
||||
|
||||
#pragma mark - Static Functions
|
||||
|
||||
+ (bool)isInConference:(LinphoneCall *)call {
|
||||
if (!call)
|
||||
return false;
|
||||
return linphone_call_params_get_local_conference_mode(linphone_call_get_current_params(call));
|
||||
}
|
||||
|
||||
+ (int)callCount {
|
||||
int count = 0;
|
||||
const MSList *calls = linphone_core_get_calls(LC);
|
||||
|
||||
while (calls != 0) {
|
||||
if (![UIHangUpButton isInConference:((LinphoneCall *)calls->data)]) {
|
||||
count++;
|
||||
}
|
||||
calls = calls->next;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
#pragma mark - Lifecycle Functions
|
||||
|
||||
- (void)initUIHangUpButton {
|
||||
[self addTarget:self action:@selector(touchUp:) forControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
[self initUIHangUpButton];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(NSCoder *)decoder {
|
||||
self = [super initWithCoder:decoder];
|
||||
if (self) {
|
||||
[self initUIHangUpButton];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[self initUIHangUpButton];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)update {
|
||||
if (linphone_core_get_calls_nb(LC) == 1 || // One call
|
||||
linphone_core_get_current_call(LC) != NULL || // In call
|
||||
linphone_core_is_in_conference(LC) || // In conference
|
||||
(linphone_core_get_conference_size(LC) > 0 && [UIHangUpButton callCount] == 0) // Only one conf
|
||||
) {
|
||||
[self setEnabled:true];
|
||||
return;
|
||||
}
|
||||
[self setEnabled:false];
|
||||
}
|
||||
|
||||
#pragma mark - Action Functions
|
||||
|
||||
- (void)touchUp:(id)sender {
|
||||
LinphoneCall *currentcall = linphone_core_get_current_call(LC);
|
||||
if (linphone_core_is_in_conference(LC) || // In conference
|
||||
(linphone_core_get_conference_size(LC) > 0 && [UIHangUpButton callCount] == 0) // Only one conf
|
||||
) {
|
||||
LinphoneManager.instance.conf = TRUE;
|
||||
linphone_core_terminate_conference(LC);
|
||||
} else if (currentcall != NULL) {
|
||||
[CallManager.instance terminateCallWithCall:currentcall];
|
||||
} else {
|
||||
const MSList *calls = linphone_core_get_calls(LC);
|
||||
if (bctbx_list_size(calls) == 1) { // Only one call
|
||||
[CallManager.instance terminateCallWithCall:(calls->data)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -21,6 +21,7 @@
|
|||
#import "LinphoneManager.h"
|
||||
#import "PhoneMainView.h"
|
||||
#import "Utils.h"
|
||||
#import "linphoneapp-Swift.h"
|
||||
|
||||
@implementation UIHistoryCell
|
||||
|
||||
|
|
@ -59,10 +60,16 @@
|
|||
if (callLog != NULL) {
|
||||
HistoryDetailsView *view = VIEW(HistoryDetailsView);
|
||||
if (linphone_call_log_get_call_id(callLog) != NULL) {
|
||||
// Go to History details view
|
||||
[view setCallLogId:[NSString stringWithUTF8String:linphone_call_log_get_call_id(callLog)]];
|
||||
if (linphone_call_log_was_conference(callLog)) {
|
||||
ConferenceHistoryDetailsView *view = VIEW(ConferenceHistoryDetailsView);
|
||||
[PhoneMainView.instance changeCurrentView:view.compositeViewDescription];
|
||||
[view setCallLogWithCallLog:callLog];
|
||||
} else {
|
||||
// Go to History details view
|
||||
[view setCallLogId:[NSString stringWithUTF8String:linphone_call_log_get_call_id(callLog)]];
|
||||
[PhoneMainView.instance changeCurrentView:view.compositeViewDescription];
|
||||
}
|
||||
}
|
||||
[PhoneMainView.instance changeCurrentView:view.compositeViewDescription];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -80,32 +87,39 @@
|
|||
LOGW(@"Cannot update history cell: null callLog");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Set up the cell...
|
||||
const LinphoneAddress *addr;
|
||||
UIImage *image;
|
||||
if (linphone_call_log_get_dir(callLog) == LinphoneCallIncoming) {
|
||||
if (linphone_call_log_get_status(callLog) != LinphoneCallMissed) {
|
||||
image = [UIImage imageNamed:@"call_status_incoming.png"];
|
||||
} else {
|
||||
image = [UIImage imageNamed:@"call_status_missed.png"];
|
||||
}
|
||||
addr = linphone_call_log_get_from_address(callLog);
|
||||
if (linphone_call_log_was_conference(callLog)) {
|
||||
const char *subject = linphone_conference_info_get_subject(linphone_call_log_get_conference_info(callLog));
|
||||
displayNameLabel.text = [NSString stringWithFormat:@"%s",subject];
|
||||
[_avatarImage setImage:[UIImage imageNamed:@"voip_multiple_contacts_avatar"]];
|
||||
_stateImage.hidden = true;
|
||||
} else {
|
||||
image = [UIImage imageNamed:@"call_status_outgoing.png"];
|
||||
addr = linphone_call_log_get_to_address(callLog);
|
||||
}
|
||||
_stateImage.image = image;
|
||||
|
||||
[ContactDisplay setDisplayNameLabel:displayNameLabel forAddress:addr];
|
||||
|
||||
size_t count = bctbx_list_size(linphone_call_log_get_user_data(callLog)) + 1;
|
||||
if (count > 1) {
|
||||
displayNameLabel.text =
|
||||
_stateImage.hidden = false;
|
||||
const LinphoneAddress *addr;
|
||||
UIImage *image;
|
||||
if (linphone_call_log_get_dir(callLog) == LinphoneCallIncoming) {
|
||||
if (linphone_call_log_get_status(callLog) != LinphoneCallMissed) {
|
||||
image = [UIImage imageNamed:@"call_status_incoming.png"];
|
||||
} else {
|
||||
image = [UIImage imageNamed:@"call_status_missed.png"];
|
||||
}
|
||||
addr = linphone_call_log_get_from_address(callLog);
|
||||
} else {
|
||||
image = [UIImage imageNamed:@"call_status_outgoing.png"];
|
||||
addr = linphone_call_log_get_to_address(callLog);
|
||||
}
|
||||
_stateImage.image = image;
|
||||
[ContactDisplay setDisplayNameLabel:displayNameLabel forAddress:addr];
|
||||
|
||||
size_t count = bctbx_list_size(linphone_call_log_get_user_data(callLog)) + 1;
|
||||
if (count > 1) {
|
||||
displayNameLabel.text =
|
||||
[displayNameLabel.text stringByAppendingString:[NSString stringWithFormat:@" (%lu)", count]];
|
||||
}
|
||||
|
||||
[_avatarImage setImage:[FastAddressBook imageForAddress:addr] bordered:NO withRoundedRadius:YES];
|
||||
}
|
||||
|
||||
[_avatarImage setImage:[FastAddressBook imageForAddress:addr] bordered:NO withRoundedRadius:YES];
|
||||
}
|
||||
|
||||
- (void)setEditing:(BOOL)editing {
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#import "UIToggleButton.h"
|
||||
|
||||
#include "linphone/linphonecore.h"
|
||||
|
||||
typedef enum _UIPauseButtonType {
|
||||
UIPauseButtonType_CurrentCall,
|
||||
UIPauseButtonType_Call,
|
||||
UIPauseButtonType_Conference
|
||||
} UIPauseButtonType;
|
||||
|
||||
@interface UIPauseButton : UIToggleButton<UIToggleButtonDelegate> {
|
||||
@private
|
||||
UIPauseButtonType type;
|
||||
LinphoneCall* call;
|
||||
}
|
||||
|
||||
- (void)setType:(UIPauseButtonType) type call:(LinphoneCall*)call;
|
||||
|
||||
@end
|
||||
|
|
@ -1,181 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#import "linphoneapp-Swift.h"
|
||||
#import "UIPauseButton.h"
|
||||
#import "LinphoneManager.h"
|
||||
#import "Utils.h"
|
||||
|
||||
@implementation UIPauseButton
|
||||
|
||||
#pragma mark - Lifecycle Functions
|
||||
|
||||
- (void)initUIPauseButton {
|
||||
type = UIPauseButtonType_CurrentCall;
|
||||
}
|
||||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
[self initUIPauseButton];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(NSCoder *)decoder {
|
||||
self = [super initWithCoder:decoder];
|
||||
if (self) {
|
||||
[self initUIPauseButton];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[self initUIPauseButton];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Static Functions
|
||||
|
||||
+ (bool)isInConference:(LinphoneCall *)call {
|
||||
if (!call)
|
||||
return false;
|
||||
return linphone_call_params_get_local_conference_mode(linphone_call_get_current_params(call));
|
||||
}
|
||||
|
||||
+ (LinphoneCall *)getCall {
|
||||
LinphoneCall *currentCall = linphone_core_get_current_call(LC);
|
||||
if (currentCall == nil && linphone_core_get_calls_nb(LC) == 1) {
|
||||
currentCall = (LinphoneCall *)linphone_core_get_calls(LC)->data;
|
||||
}
|
||||
return currentCall;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)setType:(UIPauseButtonType)atype call:(LinphoneCall *)acall {
|
||||
type = atype;
|
||||
call = acall;
|
||||
}
|
||||
|
||||
#pragma mark - UIToggleButtonDelegate Functions
|
||||
|
||||
- (void)onOn {
|
||||
switch (type) {
|
||||
case UIPauseButtonType_Call: {
|
||||
if (call != nil) {
|
||||
if ([CallManager callKitEnabled]) {
|
||||
[CallManager.instance setHeldWithCall:call hold:true];
|
||||
} else {
|
||||
CallManager.instance.speakerBeforePause = [CallManager.instance isSpeakerEnabled];
|
||||
linphone_call_pause(call);
|
||||
}
|
||||
} else {
|
||||
LOGW(@"Cannot toggle pause buttton, because no current call");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case UIPauseButtonType_Conference: {
|
||||
linphone_conference_leave(CallManager.instance.getConference);
|
||||
// Fake event
|
||||
[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneCallUpdate object:self];
|
||||
break;
|
||||
}
|
||||
case UIPauseButtonType_CurrentCall: {
|
||||
LinphoneCall *currentCall = [UIPauseButton getCall];
|
||||
if (currentCall != nil) {
|
||||
if ([CallManager callKitEnabled]) {
|
||||
[CallManager.instance setHeldWithCall:currentCall hold:true];
|
||||
} else {
|
||||
CallManager.instance.speakerBeforePause = [CallManager.instance isSpeakerEnabled];
|
||||
linphone_call_pause(currentCall);
|
||||
}
|
||||
} else {
|
||||
LOGW(@"Cannot toggle pause buttton, because no current call");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onOff {
|
||||
switch (type) {
|
||||
case UIPauseButtonType_Call: {
|
||||
if (call != nil) {
|
||||
if ([CallManager callKitEnabled]) {
|
||||
[CallManager.instance setHeldWithCall:call hold:false];
|
||||
} else {
|
||||
linphone_call_resume(call);
|
||||
}
|
||||
} else {
|
||||
LOGW(@"Cannot toggle pause buttton, because no current call");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case UIPauseButtonType_Conference: {
|
||||
linphone_conference_enter(CallManager.instance.getConference);
|
||||
// Fake event
|
||||
[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneCallUpdate object:self];
|
||||
break;
|
||||
}
|
||||
case UIPauseButtonType_CurrentCall: {
|
||||
LinphoneCall *currentCall = [UIPauseButton getCall];
|
||||
if ([CallManager callKitEnabled]) {
|
||||
[CallManager.instance setHeldWithCall:currentCall hold:false];
|
||||
} else {
|
||||
linphone_call_resume(currentCall);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (bool)onUpdate {
|
||||
bool ret = false;
|
||||
LinphoneCall *c = call;
|
||||
switch (type) {
|
||||
case UIPauseButtonType_Conference: {
|
||||
self.enabled = CallManager.instance.getConference && (linphone_conference_get_participant_count(CallManager.instance.getConference)> 0);
|
||||
if (self.enabled) {
|
||||
ret = (!CallManager.instance.isInConference);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case UIPauseButtonType_CurrentCall:
|
||||
c = [UIPauseButton getCall];
|
||||
case UIPauseButtonType_Call: {
|
||||
if (c != nil) {
|
||||
LinphoneCallState state = linphone_call_get_state(c);
|
||||
ret = (state == LinphoneCallPaused || state == LinphoneCallPausing);
|
||||
self.enabled = !linphone_core_sound_resources_locked(LC) &&
|
||||
(state == LinphoneCallPaused || state == LinphoneCallPausing ||
|
||||
state == LinphoneCallStreamsRunning);
|
||||
} else {
|
||||
self.enabled = FALSE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#import "linphoneapp-Swift.h"
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
#import "UISpeakerButton.h"
|
||||
#import "Utils.h"
|
||||
#import "LinphoneManager.h"
|
||||
|
||||
#include "linphone/linphonecore.h"
|
||||
|
||||
@implementation UISpeakerButton
|
||||
|
||||
INIT_WITH_COMMON_CF {
|
||||
[NSNotificationCenter.defaultCenter addObserver:self
|
||||
selector:@selector(audioRouteChangeListenerCallback:)
|
||||
name:AVAudioSessionRouteChangeNotification
|
||||
object:nil];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)onOn {
|
||||
[CallManager.instance changeRouteToSpeaker];
|
||||
}
|
||||
|
||||
- (void)onOff {
|
||||
[CallManager.instance changeRouteToDefault];
|
||||
}
|
||||
|
||||
|
||||
- (void)dealloc {
|
||||
[NSNotificationCenter.defaultCenter removeObserver:self];
|
||||
}
|
||||
|
||||
#pragma mark - UIToggleButtonDelegate Functions
|
||||
|
||||
- (void)audioRouteChangeListenerCallback:(NSNotification *)notif {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self update];});
|
||||
}
|
||||
|
||||
- (bool)onUpdate {
|
||||
return [CallManager.instance isSpeakerEnabled];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface VideoZoomHandler : NSObject {
|
||||
float zoomLevel, cx, cy;
|
||||
UIView* videoView;
|
||||
}
|
||||
|
||||
- (void) setup: (UIView*) videoView;
|
||||
- (void) resetZoom;
|
||||
|
||||
@end
|
||||
|
|
@ -1,111 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#import "VideoZoomHandler.h"
|
||||
#include "linphone/linphonecore.h"
|
||||
#import "LinphoneManager.h"
|
||||
|
||||
@implementation VideoZoomHandler
|
||||
|
||||
- (void)zoomInOut:(UITapGestureRecognizer *)reco {
|
||||
if (zoomLevel != 1)
|
||||
zoomLevel = 1;
|
||||
else
|
||||
zoomLevel = 2;
|
||||
|
||||
if (zoomLevel != 1) {
|
||||
CGPoint point = [reco locationInView:videoView];
|
||||
cx = point.x / videoView.frame.size.width;
|
||||
cy = 1 - point.y / videoView.frame.size.height;
|
||||
} else {
|
||||
cx = cy = 0.5;
|
||||
}
|
||||
linphone_call_zoom_video(linphone_core_get_current_call(LC), zoomLevel, &cx, &cy);
|
||||
}
|
||||
|
||||
- (void)videoPan:(UIPanGestureRecognizer *)reco {
|
||||
if (zoomLevel <= 1.0)
|
||||
return;
|
||||
|
||||
float x, y;
|
||||
CGPoint translation = [reco translationInView:videoView];
|
||||
if ([reco state] == UIGestureRecognizerStateEnded) {
|
||||
cx -= translation.x / videoView.frame.size.width;
|
||||
cy += translation.y / videoView.frame.size.height;
|
||||
x = cx;
|
||||
y = cy;
|
||||
} else if ([reco state] == UIGestureRecognizerStateChanged) {
|
||||
x = cx - translation.x / videoView.frame.size.width;
|
||||
y = cy + translation.y / videoView.frame.size.height;
|
||||
[reco setTranslation:CGPointMake(0, 0) inView:videoView];
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
linphone_call_zoom_video(linphone_core_get_current_call(LC), zoomLevel, &x, &y);
|
||||
cx = x;
|
||||
cy = y;
|
||||
}
|
||||
|
||||
- (void)pinch:(UIPinchGestureRecognizer *)reco {
|
||||
float s = zoomLevel;
|
||||
// CGPoint point = [reco locationInView:videoGroup];
|
||||
// float ccx = cx + (point.x / videoGroup.frame.size.width - 0.5) / s;
|
||||
// float ccy = cy - (point.y / videoGroup.frame.size.height - 0.5) / s;
|
||||
if ([reco state] == UIGestureRecognizerStateEnded) {
|
||||
zoomLevel = MAX(MIN(zoomLevel * reco.scale, 3.0), 1.0);
|
||||
s = zoomLevel;
|
||||
// cx = ccx;
|
||||
// cy = ccy;
|
||||
} else if ([reco state] == UIGestureRecognizerStateChanged) {
|
||||
s = zoomLevel * reco.scale;
|
||||
s = MAX(MIN(s, 3.0), 1.0);
|
||||
} else if ([reco state] == UIGestureRecognizerStateBegan) {
|
||||
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
linphone_call_zoom_video(linphone_core_get_current_call(LC), s, &cx, &cy);
|
||||
}
|
||||
|
||||
- (void)resetZoom {
|
||||
zoomLevel = 1;
|
||||
cx = cy = 0.5;
|
||||
}
|
||||
|
||||
- (void)setup:(UIView *)view {
|
||||
videoView = view;
|
||||
|
||||
UITapGestureRecognizer *doubleFingerTap =
|
||||
[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(zoomInOut:)];
|
||||
[doubleFingerTap setNumberOfTapsRequired:2];
|
||||
[doubleFingerTap setNumberOfTouchesRequired:1];
|
||||
[videoView addGestureRecognizer:doubleFingerTap];
|
||||
|
||||
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(videoPan:)];
|
||||
[videoView addGestureRecognizer:pan];
|
||||
UIPinchGestureRecognizer *pinchReco =
|
||||
[[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinch:)];
|
||||
[videoView addGestureRecognizer:pinchReco];
|
||||
|
||||
[self resetZoom];
|
||||
}
|
||||
|
||||
@end
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -32,6 +32,11 @@
|
|||
+ (void)log:(OrtpLogLevel)severity file:(const char *)file line:(int)line format:(NSString *)format, ...;
|
||||
+ (void)enableLogs:(OrtpLogLevel)level;
|
||||
+ (void)directLog:(OrtpLogLevel)level text:(NSString *)text;
|
||||
+ (void)d:(NSString *)text;
|
||||
+ (void)i:(NSString *)text;
|
||||
+ (void)w:(NSString *)text;
|
||||
+ (void)e:(NSString *)text;
|
||||
+ (void)f:(NSString *)text;
|
||||
|
||||
void linphone_iphone_log_handler(const char *domain, OrtpLogLevel lev, const char *fmt, va_list args);
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -27,10 +27,6 @@
|
|||
#import "AboutView.h"
|
||||
#import "AssistantLinkView.h"
|
||||
#import "AssistantView.h"
|
||||
#import "CallIncomingView.h"
|
||||
#import "CallOutgoingView.h"
|
||||
#import "CallSideMenuView.h"
|
||||
#import "CallView.h"
|
||||
#import "ChatConversationCreateView.h"
|
||||
#import "ChatConversationInfoView.h"
|
||||
#import "ChatConversationImdnView.h"
|
||||
|
|
@ -78,7 +74,7 @@
|
|||
|
||||
@end
|
||||
|
||||
@interface PhoneMainView : UIViewController<IncomingCallViewDelegate, MFMessageComposeViewControllerDelegate> {
|
||||
@interface PhoneMainView : UIViewController<MFMessageComposeViewControllerDelegate> {
|
||||
@private
|
||||
NSMutableArray *inhibitedEvents;
|
||||
}
|
||||
|
|
@ -96,6 +92,7 @@
|
|||
|
||||
- (void)changeCurrentView:(UICompositeViewDescription *)view;
|
||||
- (UIViewController*)popCurrentView;
|
||||
- (UIViewController *)popView:(UICompositeViewDescription *)view;
|
||||
- (UIViewController *)popToView:(UICompositeViewDescription *)currentView;
|
||||
- (void) setPreviousViewName:(NSString*)previous;
|
||||
- (NSString*) getPreviousViewName;
|
||||
|
|
|
|||
|
|
@ -17,12 +17,13 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#import "linphoneapp-Swift.h"
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
#import <AudioToolbox/AudioServices.h>
|
||||
#import "LinphoneAppDelegate.h"
|
||||
#import "Log.h"
|
||||
#import "PhoneMainView.h"
|
||||
#import "linphoneapp-Swift.h"
|
||||
|
||||
|
||||
static RootViewManager *rootViewManagerInstance = nil;
|
||||
|
||||
|
|
@ -373,8 +374,16 @@ static RootViewManager *rootViewManagerInstance = nil;
|
|||
}
|
||||
break;
|
||||
}
|
||||
case LinphoneCallOutgoingInit: {
|
||||
[self changeCurrentView:CallOutgoingView.compositeViewDescription];
|
||||
case LinphoneCallOutgoingInit:
|
||||
case LinphoneCallOutgoingEarlyMedia:
|
||||
case LinphoneCallOutgoingProgress:
|
||||
case LinphoneCallOutgoingRinging: {
|
||||
CallAppData *data = [CallManager getAppDataWithCall:call];
|
||||
if (!data.isConference) {
|
||||
OutgoingCallView *v = VIEW(OutgoingCallView);
|
||||
[self changeCurrentView:OutgoingCallView.compositeViewDescription];
|
||||
[v setCallWithCall:call];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LinphoneCallPausedByRemote:
|
||||
|
|
@ -382,47 +391,16 @@ static RootViewManager *rootViewManagerInstance = nil;
|
|||
if (![LinphoneManager.instance isCTCallCenterExist]) {
|
||||
/*only register CT call center CB for connected call*/
|
||||
[LinphoneManager.instance setupGSMInteraction];
|
||||
[[UIDevice currentDevice] setProximityMonitoringEnabled:!([CallManager.instance isSpeakerEnabled] || [CallManager.instance isBluetoothEnabled])];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LinphoneCallStreamsRunning: {
|
||||
[self changeCurrentView:CallView.compositeViewDescription];
|
||||
break;
|
||||
}
|
||||
case LinphoneCallUpdatedByRemote: {
|
||||
const LinphoneCallParams *current = linphone_call_get_current_params(call);
|
||||
const LinphoneCallParams *remote = linphone_call_get_remote_params(call);
|
||||
|
||||
if (linphone_call_params_video_enabled(current) && !linphone_call_params_video_enabled(remote)) {
|
||||
[self changeCurrentView:CallView.compositeViewDescription];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LinphoneCallError: {
|
||||
[self displayCallError:call message:message];
|
||||
}
|
||||
case LinphoneCallEnd: {
|
||||
const MSList *calls = linphone_core_get_calls(LC);
|
||||
if (!calls || calls->data == call) {
|
||||
while ((currentView == CallView.compositeViewDescription) ||
|
||||
(currentView == CallIncomingView.compositeViewDescription) ||
|
||||
(currentView == CallOutgoingView.compositeViewDescription)) {
|
||||
[self popCurrentView];
|
||||
}
|
||||
} else {
|
||||
[self changeCurrentView:CallView.compositeViewDescription];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LinphoneCallEarlyUpdatedByRemote:
|
||||
case LinphoneCallEarlyUpdating:
|
||||
case LinphoneCallIdle:
|
||||
break;
|
||||
case LinphoneCallOutgoingEarlyMedia:
|
||||
case LinphoneCallOutgoingProgress: {
|
||||
break;
|
||||
}
|
||||
case LinphoneCallReleased:
|
||||
if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
|
@ -431,7 +409,6 @@ static RootViewManager *rootViewManagerInstance = nil;
|
|||
});
|
||||
}
|
||||
break;
|
||||
case LinphoneCallOutgoingRinging:
|
||||
case LinphoneCallPaused:
|
||||
case LinphoneCallPausing:
|
||||
case LinphoneCallRefered:
|
||||
|
|
@ -634,6 +611,15 @@ static RootViewManager *rootViewManagerInstance = nil;
|
|||
return [mainViewController getCurrentViewController];
|
||||
}
|
||||
|
||||
- (UIViewController *)popView:(UICompositeViewDescription *)view {
|
||||
NSMutableArray *viewStack = [RootViewManager instance].viewDescriptionStack;
|
||||
while (viewStack.count > 0 && [[viewStack lastObject] equal:view]) {
|
||||
[viewStack removeLastObject];
|
||||
}
|
||||
return [self popToView:viewStack.lastObject ?: DialerView.compositeViewDescription];
|
||||
}
|
||||
|
||||
|
||||
- (void)changeCurrentView:(UICompositeViewDescription *)view {
|
||||
[self _changeCurrentView:view transition:nil animated:ANIMATED];
|
||||
}
|
||||
|
|
@ -765,10 +751,10 @@ static RootViewManager *rootViewManagerInstance = nil;
|
|||
[CallManager.instance acceptCallWithCall:call hasVideo:YES];
|
||||
} else {
|
||||
AudioServicesPlaySystemSound(lm.sounds.vibrate);
|
||||
CallIncomingView *view = VIEW(CallIncomingView);
|
||||
IncomingCallView *view = VIEW(IncomingCallView);
|
||||
[self changeCurrentView:view.compositeViewDescription];
|
||||
[view setCall:call];
|
||||
[view setDelegate:self];
|
||||
[view setCallWithCall:call];
|
||||
//CDFIX [view setDelegate:self];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#import "ShopView.h"
|
||||
#import "LinphoneManager.h"
|
||||
#import "RecordingsListView.h"
|
||||
#import "linphoneapp-Swift.h"
|
||||
|
||||
@implementation SideMenuEntry
|
||||
|
||||
|
|
@ -101,6 +102,15 @@
|
|||
changeCurrentView:ShopView.compositeViewDescription];
|
||||
}]];
|
||||
}
|
||||
|
||||
[_sideMenuEntries addObject:[[SideMenuEntry alloc] initWithTitle:VoipTexts.conference_scheduled
|
||||
image:[UIImage imageNamed:@"voip_conference_new.png"]
|
||||
tapBlock:^() {
|
||||
[PhoneMainView.instance
|
||||
changeCurrentView:ScheduledConferencesView.compositeViewDescription];
|
||||
|
||||
}]];
|
||||
|
||||
[_sideMenuEntries addObject:[[SideMenuEntry alloc] initWithTitle:NSLocalizedString(@"About", nil)
|
||||
image:[UIImage imageNamed:@"menu_about.png"]
|
||||
tapBlock:^() {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ import AVFoundation
|
|||
@objc class CallAppData: NSObject {
|
||||
@objc var batteryWarningShown = false
|
||||
@objc var videoRequested = false /*set when user has requested for video*/
|
||||
@objc var isConference = true
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -245,6 +247,19 @@ import AVFoundation
|
|||
callParams.recordFile = writablePath
|
||||
|
||||
|
||||
if let chatView : ChatConversationView = PhoneMainView.instance().VIEW(ChatConversationView.compositeViewDescription()), chatView.isVoiceRecording {
|
||||
Log.directLog(BCTBX_LOG_MESSAGE, text: "Voice recording in progress, stopping it befoce accepting the call.")
|
||||
chatView.stopVoiceRecording()
|
||||
}
|
||||
|
||||
if (call.callLog?.wasConference() == true) {
|
||||
// Prevent incoming group call to start in audio only layout
|
||||
// Do the same as the conference waiting room
|
||||
callParams.videoEnabled = true
|
||||
callParams.videoDirection = Core.get().videoActivationPolicy?.automaticallyInitiate == true ? .SendRecv : .RecvOnly
|
||||
Log.i("[Context] Enabling video on call params to prevent audio-only layout when answering")
|
||||
}
|
||||
|
||||
try call.acceptWithParams(params: callParams)
|
||||
} catch {
|
||||
Log.directLog(BCTBX_LOG_ERROR, text: "accept call failed \(error)")
|
||||
|
|
@ -252,32 +267,41 @@ import AVFoundation
|
|||
}
|
||||
|
||||
// for outgoing call. There is not yet callId
|
||||
@objc func startCall(addr: OpaquePointer?, isSas: Bool) {
|
||||
@objc func startCall(addr: OpaquePointer?, isSas: Bool, isVideo: Bool, isConference: Bool = false) {
|
||||
if (addr == nil) {
|
||||
print("Can not start a call with null address!")
|
||||
return
|
||||
}
|
||||
|
||||
let sAddr = Address.getSwiftObject(cObject: addr!)
|
||||
if (CallManager.callKitEnabled() && !CallManager.instance().nextCallIsTransfer && !isInConference()) {
|
||||
if (CallManager.callKitEnabled() && !CallManager.instance().nextCallIsTransfer && lc?.conference?.isIn != true) {
|
||||
let uuid = UUID()
|
||||
let name = FastAddressBook.displayName(for: addr) ?? "unknow"
|
||||
let handle = CXHandle(type: .generic, value: sAddr.asStringUriOnly())
|
||||
let startCallAction = CXStartCallAction(call: uuid, handle: handle)
|
||||
let transaction = CXTransaction(action: startCallAction)
|
||||
|
||||
let callInfo = CallInfo.newOutgoingCallInfo(addr: sAddr, isSas: isSas, displayName: name)
|
||||
let callInfo = CallInfo.newOutgoingCallInfo(addr: sAddr, isSas: isSas, displayName: name, isVideo: isVideo, isConference:isConference)
|
||||
providerDelegate.callInfos.updateValue(callInfo, forKey: uuid)
|
||||
providerDelegate.uuids.updateValue(uuid, forKey: "")
|
||||
|
||||
setHeldOtherCalls(exceptCallid: "")
|
||||
requestTransaction(transaction, action: "startCall")
|
||||
}else {
|
||||
try? doCall(addr: sAddr, isSas: isSas)
|
||||
try? doCall(addr: sAddr, isSas: isSas, isVideo:isVideo, isConference:isConference)
|
||||
}
|
||||
}
|
||||
|
||||
func startCall(addr:String, isSas: Bool = false, isVideo: Bool, isConference: Bool = false) {
|
||||
do {
|
||||
let address = try Factory.Instance.createAddress(addr: addr)
|
||||
startCall(addr: address.getCobject,isSas: isSas, isVideo: isVideo, isConference:isConference)
|
||||
} catch {
|
||||
Log.e("[CallManager] unable to create address for a new outgoing call : \(addr) \(error) ")
|
||||
}
|
||||
}
|
||||
|
||||
func doCall(addr: Address, isSas: Bool) throws {
|
||||
func doCall(addr: Address, isSas: Bool, isVideo: Bool, isConference:Bool = false) throws {
|
||||
let displayName = FastAddressBook.displayName(for: addr.getCobject)
|
||||
|
||||
let lcallParams = try CallManager.instance().lc!.createCallParams(call: nil)
|
||||
|
|
@ -306,6 +330,18 @@ import AVFoundation
|
|||
if (isSas) {
|
||||
lcallParams.mediaEncryption = .ZRTP
|
||||
}
|
||||
if (isConference) {
|
||||
if (ConferenceWaitingRoomViewModel.sharedModel.joinLayout.value! != .AudioOnly) {
|
||||
lcallParams.videoEnabled = true
|
||||
lcallParams.videoDirection = ConferenceWaitingRoomViewModel.sharedModel.isVideoEnabled.value == true ? .SendRecv : .RecvOnly
|
||||
lcallParams.conferenceVideoLayout = ConferenceWaitingRoomViewModel.sharedModel.joinLayout.value! == .Grid ? .Grid : .ActiveSpeaker
|
||||
} else {
|
||||
lcallParams.videoEnabled = false
|
||||
}
|
||||
} else {
|
||||
lcallParams.videoEnabled = isVideo
|
||||
}
|
||||
|
||||
let call = CallManager.instance().lc!.inviteAddressWithParams(addr: addr, params: lcallParams)
|
||||
if (call != nil) {
|
||||
// The LinphoneCallAppData object should be set on call creation with callback
|
||||
|
|
@ -316,6 +352,7 @@ import AVFoundation
|
|||
Log.directLog(BCTBX_LOG_ERROR, text: "New call instanciated but app data was not set. Expect it to crash.")
|
||||
/* will be used later to notify user if video was not activated because of the linphone core*/
|
||||
} else {
|
||||
data!.isConference = isConference
|
||||
data!.videoRequested = lcallParams.videoEnabled
|
||||
CallManager.setAppData(sCall: call!, appData: data)
|
||||
}
|
||||
|
|
@ -396,6 +433,14 @@ import AVFoundation
|
|||
}
|
||||
|
||||
func setHeld(call: Call, hold: Bool) {
|
||||
|
||||
#if targetEnvironment(simulator)
|
||||
if (hold) {
|
||||
try?call.pause()
|
||||
} else {
|
||||
try?call.resume()
|
||||
}
|
||||
#else
|
||||
let callid = call.callLog?.callId ?? ""
|
||||
let uuid = providerDelegate.uuids["\(callid)"]
|
||||
if (uuid == nil) {
|
||||
|
|
@ -405,6 +450,7 @@ import AVFoundation
|
|||
let setHeldAction = CXSetHeldCallAction(call: uuid!, onHold: hold)
|
||||
let transaction = CXTransaction(action: setHeldAction)
|
||||
requestTransaction(transaction, action: "setHeld")
|
||||
#endif
|
||||
}
|
||||
|
||||
@objc func setHeldOtherCalls(exceptCallid: String) {
|
||||
|
|
@ -469,19 +515,9 @@ import AVFoundation
|
|||
}
|
||||
}
|
||||
|
||||
func onConferenceStateChanged(core: Core, conference: Conference, state: Conference.State) {
|
||||
if (state == .Terminated) {
|
||||
CallManager.instance().conference = nil
|
||||
}
|
||||
}
|
||||
|
||||
func onAudioDevicesListUpdated(core: Core) {
|
||||
let bluetoothAvailable = isBluetoothAvailable();
|
||||
|
||||
var dict = Dictionary<String, Bool>()
|
||||
dict["available"] = bluetoothAvailable
|
||||
NotificationCenter.default.post(name: Notification.Name("LinphoneBluetoothAvailabilityUpdate"), object: self, userInfo: dict)
|
||||
|
||||
func isConferenceCall(call:Call) -> Bool {
|
||||
let remoteAddress = call.remoteAddress?.asStringUriOnly()
|
||||
return remoteAddress?.contains("focus") == true || remoteAddress?.contains("audiovideo") == true
|
||||
}
|
||||
|
||||
func onCallStateChanged(core: Core, call: Call, state cstate: Call.State, message: String) {
|
||||
|
|
@ -496,12 +532,40 @@ import AVFoundation
|
|||
let appData = CallAppData()
|
||||
CallManager.setAppData(sCall: call, appData: appData)
|
||||
}
|
||||
|
||||
if let conference = call.conference, ConferenceViewModel.shared.conference.value == nil {
|
||||
Log.i("[Call] Found conference attached to call and no conference in dedicated view model, init & configure it")
|
||||
ConferenceViewModel.shared.initConference(conference)
|
||||
ConferenceViewModel.shared.configureConference(conference)
|
||||
}
|
||||
|
||||
switch cstate {
|
||||
case .IncomingReceived:
|
||||
let addr = call.remoteAddress;
|
||||
let displayName = FastAddressBook.displayName(for: addr?.getCobject) ?? "Unknown"
|
||||
let addr = call.remoteAddress
|
||||
var displayName = ""
|
||||
let isConference = isConferenceCall(call: call)
|
||||
let isEarlyConference = isConference && CallsViewModel.shared.currentCallData.value??.isConferenceCall.value != true // Conference info not be received yet.
|
||||
if (isConference) {
|
||||
if (isEarlyConference) {
|
||||
displayName = VoipTexts.conference_incoming_title
|
||||
} else {
|
||||
displayName = "\(VoipTexts.conference_incoming_title): \(CallsViewModel.shared.currentCallData.value??.remoteConferenceSubject.value ?? "") (\(CallsViewModel.shared.currentCallData.value??.conferenceParticipantsCountLabel.value ?? ""))"
|
||||
}
|
||||
} else {
|
||||
displayName = FastAddressBook.displayName(for: addr?.getCobject) ?? "Unknown"
|
||||
}
|
||||
|
||||
if (CallManager.callKitEnabled()) {
|
||||
if (isEarlyConference) {
|
||||
CallsViewModel.shared.currentCallData.readCurrentAndObserve { _ in
|
||||
let uuid = CallManager.instance().providerDelegate.uuids["\(callId!)"]
|
||||
if (uuid != nil) {
|
||||
displayName = "\(VoipTexts.conference_incoming_title): \(CallsViewModel.shared.currentCallData.value??.remoteConferenceSubject.value ?? "") (\(CallsViewModel.shared.currentCallData.value??.conferenceParticipantsCountLabel.value ?? ""))"
|
||||
CallManager.instance().providerDelegate.updateCall(uuid: uuid!, handle: addr!.asStringUriOnly(), hasVideo: video, displayName: displayName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let uuid = CallManager.instance().providerDelegate.uuids["\(callId!)"]
|
||||
if (uuid != nil) {
|
||||
// Tha app is now registered, updated the call already existed.
|
||||
|
|
@ -672,116 +736,46 @@ import AVFoundation
|
|||
return speakerCard != nil ? speakerCard : earpieceCard
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Conference
|
||||
|
||||
@objc func hostConference() -> Bool {
|
||||
return conference != nil
|
||||
}
|
||||
// Local Conference
|
||||
|
||||
func addAllToConference() {
|
||||
if (conference == nil) {
|
||||
guard let cp = try?lc?.createConferenceParams(conference: conference) else {
|
||||
Log.directLog(BCTBX_LOG_ERROR, text: "Unable to create conference parameters")
|
||||
@objc func startLocalConference() {
|
||||
if (CallManager.callKitEnabled()) {
|
||||
let calls = lc?.calls
|
||||
if (calls == nil || calls!.isEmpty) {
|
||||
return
|
||||
}
|
||||
if let currentCall = lc?.currentCall, let currentParams = currentCall.currentParams {
|
||||
cp.videoEnabled = currentParams.videoEnabled
|
||||
}
|
||||
conference = try?lc?.createConferenceWithParams(params: cp)
|
||||
}
|
||||
lc?.calls.forEach { call in
|
||||
if (call.conference == nil || call.conference?.participantCount == 1) {
|
||||
try?conference?.addParticipant(call: call)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func getConference() -> OpaquePointer? {
|
||||
guard let core = lc else {
|
||||
return nil
|
||||
}
|
||||
return (core.conference != nil) ? core.conference?.getCobject : (core.currentCall?.conference != nil) ? core.currentCall!.conference!.getCobject : nil
|
||||
}
|
||||
|
||||
func getConference() -> Conference? {
|
||||
guard let core = lc else {
|
||||
return nil
|
||||
}
|
||||
return (core.conference != nil) ? core.conference : (core.currentCall?.conference != nil) ? core.currentCall!.conference : nil
|
||||
}
|
||||
|
||||
@objc func isInConference() -> Bool {
|
||||
return isInConferenceAsHost()||isInConferenceAsGuest()
|
||||
}
|
||||
|
||||
@objc func isInConferenceAsGuest() -> Bool {
|
||||
guard let core = lc else {
|
||||
return false
|
||||
}
|
||||
return !isInConferenceAsHost() && core.currentCall != nil && core.currentCall?.conference != nil && (core.currentCall?.conference!.participantCount)! > 1
|
||||
}
|
||||
|
||||
@objc func isInConferenceAsHost() -> Bool {
|
||||
guard let core = lc else {
|
||||
return false
|
||||
}
|
||||
return core.conference?.isIn == true
|
||||
}
|
||||
|
||||
@objc func hasConferenceAsGuest() -> Bool {
|
||||
guard let core = lc else {
|
||||
return false
|
||||
}
|
||||
if (core.callsNb<=1) {
|
||||
return false
|
||||
}
|
||||
var found = false
|
||||
core.calls.forEach {
|
||||
let c = $0.conference
|
||||
if (c != nil && c!.participantCount > 1 && hostConference()) {
|
||||
found = true
|
||||
let firstCall = calls!.first?.callLog?.callId ?? ""
|
||||
let lastCall = (calls!.count > 1) ? calls!.last?.callLog?.callId ?? "" : ""
|
||||
|
||||
let currentUuid = CallManager.instance().providerDelegate.uuids["\(firstCall)"]
|
||||
if (currentUuid == nil) {
|
||||
Log.directLog(BCTBX_LOG_ERROR, text: "Can not find correspondant call to group.")
|
||||
return
|
||||
}
|
||||
|
||||
let newUuid = CallManager.instance().providerDelegate.uuids["\(lastCall)"]
|
||||
let groupAction = CXSetGroupCallAction(call: currentUuid!, callUUIDToGroupWith: newUuid)
|
||||
let transcation = CXTransaction(action: groupAction)
|
||||
requestTransaction(transcation, action: "groupCall")
|
||||
|
||||
setResumeCalls()
|
||||
} else {
|
||||
addAllToLocalConference()
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
@objc func getCallFor(participant : OpaquePointer) -> OpaquePointer? {
|
||||
let p = Participant.getSwiftObject(cObject: participant)
|
||||
guard let core = lc else {
|
||||
return nil
|
||||
}
|
||||
var call:Call? = nil
|
||||
core.calls.forEach { (callIt) in
|
||||
let c = callIt.conference
|
||||
c?.participantList.forEach { (p2) in
|
||||
if (p2.address?.asStringUriOnly() == p.address?.asStringUriOnly()) {
|
||||
call = callIt
|
||||
return
|
||||
}
|
||||
func addAllToLocalConference() {
|
||||
do {
|
||||
if let core = lc, let params = try? core.createConferenceParams(conference: nil) {
|
||||
params.videoEnabled = false // We disable video for local conferencing (cf Android)
|
||||
let conference = core.conference != nil ? core.conference : try core.createConferenceWithParams(params: params)
|
||||
try conference?.addParticipants(calls: core.calls)
|
||||
}
|
||||
} catch {
|
||||
Log.directLog(BCTBX_LOG_ERROR, text: "accept call failed \(error)")
|
||||
}
|
||||
return call?.getCobject
|
||||
}
|
||||
|
||||
@objc func inVideoConf() -> Bool {
|
||||
guard let core = lc else {
|
||||
return false
|
||||
}
|
||||
let result = isInConference() && (getConference()?.currentParams?.isVideoEnabled == true || core.currentCall?.currentParams?.videoEnabled == true)
|
||||
NSLog("cdes \(result) \(core.currentCall?.currentParams?.videoEnabled)")
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
@objc func inAudioConf() -> Bool {
|
||||
guard let core = lc else {
|
||||
return false
|
||||
}
|
||||
return core.conference?.isIn == true && core.conference != nil && core.currentCall?.conference?.currentParams?.isVideoEnabled == false
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
31
Classes/Swift/Conference/Data/Duration.swift
Normal file
31
Classes/Swift/Conference/Data/Duration.swift
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2021 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-android
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
import Foundation
|
||||
|
||||
struct Duration : Comparable {
|
||||
static func < (lhs: Duration, rhs: Duration) -> Bool {
|
||||
return lhs.value < rhs.value
|
||||
}
|
||||
|
||||
let value: Int
|
||||
let display: String
|
||||
}
|
||||
84
Classes/Swift/Conference/Data/ScheduledConferenceData.swift
Normal file
84
Classes/Swift/Conference/Data/ScheduledConferenceData.swift
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2021 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-android
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import linphonesw
|
||||
|
||||
|
||||
class ScheduledConferenceData {
|
||||
|
||||
let conferenceInfo: ConferenceInfo
|
||||
let expanded = MutableLiveData<Bool>()
|
||||
let address = MutableLiveData<String>()
|
||||
let subject = MutableLiveData<String>()
|
||||
let description = MutableLiveData<String>()
|
||||
let time = MutableLiveData<String>()
|
||||
let date = MutableLiveData<String>()
|
||||
let duration = MutableLiveData<String>()
|
||||
let organizer = MutableLiveData<String>()
|
||||
let participantsShort = MutableLiveData<String>()
|
||||
let participantsExpanded = MutableLiveData<String>()
|
||||
let rawDate : Date
|
||||
|
||||
|
||||
init (conferenceInfo: ConferenceInfo) {
|
||||
self.conferenceInfo = conferenceInfo
|
||||
expanded.value = false
|
||||
|
||||
address.value = conferenceInfo.uri?.asStringUriOnly()
|
||||
subject.value = conferenceInfo.subject
|
||||
description.value = conferenceInfo.description
|
||||
|
||||
time.value = TimestampUtils.timeToString(unixTimestamp: Double(conferenceInfo.dateTime))
|
||||
date.value = TimestampUtils.toString(unixTimestamp:Double(conferenceInfo.dateTime), onlyDate:true, shortDate:false)
|
||||
rawDate = Date(timeIntervalSince1970:TimeInterval(conferenceInfo.dateTime))
|
||||
|
||||
let durationFormatter = DateComponentsFormatter()
|
||||
durationFormatter.unitsStyle = .positional
|
||||
durationFormatter.allowedUnits = [.minute, .second ]
|
||||
durationFormatter.zeroFormattingBehavior = [ .pad ]
|
||||
duration.value = conferenceInfo.duration > 0 ? durationFormatter.string(from: TimeInterval(conferenceInfo.duration)) : nil
|
||||
|
||||
organizer.value = conferenceInfo.organizer?.addressBookEnhancedDisplayName()
|
||||
|
||||
computeParticipantsLists()
|
||||
}
|
||||
|
||||
func destroy() {
|
||||
}
|
||||
|
||||
func toggleExpand() {
|
||||
expanded.value = expanded.value == false
|
||||
}
|
||||
|
||||
private func computeParticipantsLists() {
|
||||
participantsShort.value = conferenceInfo.participants.map {(participant) in
|
||||
String(describing: participant.addressBookEnhancedDisplayName())
|
||||
}.joined(separator: ", ")
|
||||
|
||||
participantsExpanded.value = conferenceInfo.participants.map {(participant) in
|
||||
String(describing: participant.addressBookEnhancedDisplayName())+" ("+String(describing: participant.asStringUriOnly())+")"
|
||||
}.joined(separator: "\n")
|
||||
}
|
||||
|
||||
func gotoAssociatedChat() {
|
||||
|
||||
}
|
||||
}
|
||||
60
Classes/Swift/Conference/Data/TimeZoneData.swift
Normal file
60
Classes/Swift/Conference/Data/TimeZoneData.swift
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2021 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-android
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import linphonesw
|
||||
|
||||
|
||||
struct TimeZoneData : Comparable {
|
||||
let timeZone: TimeZone
|
||||
|
||||
static func == (lhs: TimeZoneData, rhs: TimeZoneData) -> Bool {
|
||||
return lhs.timeZone.identifier == rhs.timeZone.identifier
|
||||
}
|
||||
|
||||
static func < (lhs: TimeZoneData, rhs: TimeZoneData) -> Bool {
|
||||
return lhs.timeZone.secondsFromGMT() < rhs.timeZone.secondsFromGMT()
|
||||
|
||||
}
|
||||
|
||||
func descWithOffset() -> String {
|
||||
return "\(timeZone.identifier) - GMT\(timeZone.offsetInHours())"
|
||||
}
|
||||
}
|
||||
|
||||
extension TimeZone {
|
||||
|
||||
func offsetFromUTC() -> String
|
||||
{
|
||||
let localTimeZoneFormatter = DateFormatter()
|
||||
localTimeZoneFormatter.timeZone = self
|
||||
localTimeZoneFormatter.dateFormat = "Z"
|
||||
return localTimeZoneFormatter.string(from: Date())
|
||||
}
|
||||
|
||||
func offsetInHours() -> String
|
||||
{
|
||||
|
||||
let hours = secondsFromGMT()/3600
|
||||
let minutes = abs(secondsFromGMT()/60) % 60
|
||||
let tz_hr = String(format: "%+.2d:%.2d", hours, minutes) // "+hh:mm"
|
||||
return tz_hr
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2021 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-android
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* aDouble with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
import Foundation
|
||||
import linphonesw
|
||||
|
||||
class ConferenceSchedulingViewModel {
|
||||
|
||||
var core : Core { get { Core.get() } }
|
||||
static let shared = ConferenceSchedulingViewModel()
|
||||
|
||||
let subject = MutableLiveData<String>()
|
||||
let description = MutableLiveData<String>()
|
||||
|
||||
let scheduleForLater = MutableLiveData<Bool>()
|
||||
let scheduledDate = MutableLiveData<Date>()
|
||||
let scheduledTime = MutableLiveData<Date>()
|
||||
|
||||
var scheduledTimeZone = MutableLiveData<Int>()
|
||||
static let timeZones: [TimeZoneData] = computeTimeZonesList()
|
||||
|
||||
var scheduledDuration = MutableLiveData<Int>()
|
||||
static let durationList: [Duration] = computeDurationList()
|
||||
|
||||
let isEncrypted = MutableLiveData<Bool>()
|
||||
|
||||
let sendInviteViaChat = MutableLiveData<Bool>()
|
||||
let sendInviteViaEmail = MutableLiveData<Bool>()
|
||||
|
||||
let address = MutableLiveData<Address>()
|
||||
|
||||
let conferenceCreationInProgress = MutableLiveData<Bool>()
|
||||
|
||||
let conferenceCreationCompletedEvent: MutableLiveData<Pair<String?,String?>> = MutableLiveData()
|
||||
let onErrorEvent = MutableLiveData<String>()
|
||||
|
||||
let continueEnabled: MutableLiveData<Bool> = MutableLiveData()
|
||||
|
||||
let selectedAddresses = MutableLiveData<[Address]>([])
|
||||
|
||||
private var conferenceScheduler: ConferenceScheduler? = nil
|
||||
|
||||
|
||||
private var hour: Int = 0
|
||||
private var minutes: Int = 0
|
||||
|
||||
private var chatRooomDelegate : ChatRoomDelegate? = nil
|
||||
private var conferenceSchedulerDelegate : ConferenceSchedulerDelegateStub? = nil
|
||||
|
||||
var existingConfInfo:ConferenceInfo? = nil
|
||||
|
||||
init () {
|
||||
|
||||
conferenceSchedulerDelegate = ConferenceSchedulerDelegateStub(
|
||||
onStateChanged: { scheduler, state in
|
||||
Log.i("[Conference Creation] Conference scheduler state is \(state)")
|
||||
if (state == .Ready) {
|
||||
Log.i("[Conference Creation] Conference info created, address will be \(scheduler.info?.uri?.asStringUriOnly())")
|
||||
guard let conferenceAddress = scheduler.info?.uri else {
|
||||
Log.e("[Conference Creation] conference address is null")
|
||||
return
|
||||
}
|
||||
self.address.value = conferenceAddress
|
||||
|
||||
if (self.sendInviteViaChat.value == true) {
|
||||
// Send conference info even when conf is not scheduled for later
|
||||
// as the conference server doesn't invite participants automatically
|
||||
if let chatRoomParams = try?self.core.createDefaultChatRoomParams() {
|
||||
chatRoomParams.backend = ChatRoomBackend.FlexisipChat
|
||||
chatRoomParams.groupEnabled = false
|
||||
chatRoomParams.encryptionEnabled = true
|
||||
chatRoomParams.subject = self.subject.value!
|
||||
scheduler.sendInvitations(chatRoomParams: chatRoomParams)
|
||||
}
|
||||
} else {
|
||||
self.conferenceCreationInProgress.value = false
|
||||
self.conferenceCreationCompletedEvent.value = Pair(conferenceAddress.asStringUriOnly(),self.conferenceScheduler?.info?.subject)
|
||||
}
|
||||
}
|
||||
}, onInvitationsSent: { conferenceScheduler, failedInvitations in
|
||||
Log.i("[Conference Creation] Conference information successfully sent to all participants")
|
||||
self.conferenceCreationInProgress.value = false
|
||||
|
||||
if (failedInvitations.count > 0) {
|
||||
failedInvitations.forEach { address in
|
||||
Log.e("[Conference Creation] Conference information wasn't sent to participant \(address.asStringUriOnly())")
|
||||
self.onErrorEvent.value = VoipTexts.conference_schedule_info_not_sent_to_participant+" (\(address.username))"
|
||||
}
|
||||
}
|
||||
|
||||
guard let conferenceAddress = conferenceScheduler.info?.uri else {
|
||||
Log.e("[Conference Creation] conference address is null")
|
||||
return
|
||||
}
|
||||
self.conferenceCreationCompletedEvent.value = Pair(conferenceAddress.asStringUriOnly(),self.conferenceScheduler?.info?.subject)
|
||||
}
|
||||
)
|
||||
|
||||
chatRooomDelegate = ChatRoomDelegateStub(
|
||||
onStateChanged : { (room: ChatRoom, state: ChatRoom.State) -> Void in
|
||||
if (state == ChatRoom.State.Created) {
|
||||
Log.i("[Conference Creation] Chat room created")
|
||||
room.removeDelegate(delegate: self.chatRooomDelegate!)
|
||||
} else if (state == ChatRoom.State.CreationFailed) {
|
||||
Log.e("[Conference Creation] Group chat room creation has failed !")
|
||||
room.removeDelegate(delegate: self.chatRooomDelegate!)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
reset()
|
||||
|
||||
subject.observe { _ in
|
||||
self.continueEnabled.value = self.allMandatoryFieldsFilled()
|
||||
}
|
||||
scheduleForLater.observe { _ in
|
||||
self.continueEnabled.value = self.allMandatoryFieldsFilled()
|
||||
}
|
||||
scheduledDate.observe { _ in
|
||||
self.continueEnabled.value = self.allMandatoryFieldsFilled()
|
||||
}
|
||||
scheduledTime.observe { _ in
|
||||
self.continueEnabled.value = self.allMandatoryFieldsFilled()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
func reset() {
|
||||
|
||||
subject.value = ""
|
||||
scheduleForLater.value = false
|
||||
isEncrypted.value = false
|
||||
sendInviteViaChat.value = true
|
||||
sendInviteViaEmail.value = false
|
||||
let now = Date()
|
||||
scheduledTime.value = Calendar.current.date(from: Calendar.current.dateComponents([.hour, .minute, .second], from: now))
|
||||
scheduledDate.value = Calendar.current.date(from: Calendar.current.dateComponents([.year, .month, .day], from: now))
|
||||
|
||||
scheduledTimeZone.value = ConferenceSchedulingViewModel.timeZones.indices.filter {
|
||||
ConferenceSchedulingViewModel.timeZones[$0].timeZone.identifier == NSTimeZone.default.identifier
|
||||
}.first
|
||||
|
||||
scheduledDuration.value = ConferenceSchedulingViewModel.durationList.indices.filter {
|
||||
ConferenceSchedulingViewModel.durationList[$0].value == 60
|
||||
}.first
|
||||
continueEnabled.value = false
|
||||
selectedAddresses.value = []
|
||||
existingConfInfo = nil
|
||||
description.value = ""
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
func destroy() {
|
||||
conferenceScheduler?.removeDelegate(delegate: conferenceSchedulerDelegate!)
|
||||
}
|
||||
|
||||
|
||||
func gotoChatRoom() {
|
||||
|
||||
}
|
||||
|
||||
func createConference() {
|
||||
|
||||
if (selectedAddresses.value?.count == 0) {
|
||||
Log.e("[Conference Creation] Couldn't create conference without any participant!")
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
conferenceCreationInProgress.value = true
|
||||
guard let localAccount = core.defaultAccount, let localAddress = localAccount.params?.identityAddress else {
|
||||
Log.e("[Conference Creation] Couldn't get local address from default account!")
|
||||
return
|
||||
}
|
||||
|
||||
conferenceScheduler = try? Core.get().createConferenceScheduler()
|
||||
conferenceScheduler?.addDelegate(delegate: conferenceSchedulerDelegate!)
|
||||
|
||||
guard let conferenceInfo = existingConfInfo != nil ? existingConfInfo : try Factory.Instance.createConferenceInfo() else {
|
||||
Log.e("[Conference Creation/Update] Failed, unable to get conf info.")
|
||||
return
|
||||
}
|
||||
conferenceInfo.organizer = localAddress
|
||||
subject.value.map { conferenceInfo.subject = $0}
|
||||
description.value.map { conferenceInfo.description = $0}
|
||||
conferenceInfo.participants = selectedAddresses.value!
|
||||
if (scheduleForLater.value == true) {
|
||||
let timestamp = getConferenceStartTimestamp()
|
||||
conferenceInfo.dateTime = time_t(timestamp)
|
||||
scheduledDuration.value.map { conferenceInfo.duration = UInt(ConferenceSchedulingViewModel.durationList[$0].value) }
|
||||
}
|
||||
conferenceScheduler?.account = localAccount
|
||||
conferenceScheduler?.info = conferenceInfo // Will trigger the conference creation automatically
|
||||
existingConfInfo = conferenceInfo
|
||||
|
||||
} catch {
|
||||
Log.e("[Conference Creation] Failed \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private func allMandatoryFieldsFilled() -> Bool {
|
||||
return subject.value != nil && subject.value!.count > 0 && (scheduleForLater.value != true || (scheduledDate.value != nil && scheduledTime.value != nil));
|
||||
}
|
||||
|
||||
|
||||
private func getConferenceStartTimestamp() -> Double {
|
||||
return scheduleForLater.value == true ?
|
||||
scheduledDate.value!.timeIntervalSince1970 +
|
||||
scheduledTime.value!.timeIntervalSince1970 - Calendar.current.startOfDay(for: scheduledTime.value!).timeIntervalSince1970 +
|
||||
Double(ConferenceSchedulingViewModel.timeZones[scheduledTimeZone.value!].timeZone.secondsFromGMT()-TimeZone.current.secondsFromGMT())
|
||||
: Date().timeIntervalSince1970
|
||||
}
|
||||
|
||||
|
||||
private static func computeTimeZonesList() -> [TimeZoneData] {
|
||||
return TimeZone.knownTimeZoneIdentifiers.map {
|
||||
(ident) in TimeZoneData(timeZone:TimeZone(identifier:ident)!)
|
||||
}.sorted()
|
||||
}
|
||||
|
||||
private static func computeDurationList() -> [Duration] {
|
||||
return [Duration(value: 30, display: "30min"), Duration(value: 60, display: "1h"), Duration(value: 120, display: "2h")]
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2021 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-android
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* aDouble with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
import Foundation
|
||||
import linphonesw
|
||||
|
||||
class ConferenceWaitingRoomViewModel: ControlsViewModel {
|
||||
|
||||
|
||||
static let sharedModel = ConferenceWaitingRoomViewModel()
|
||||
|
||||
|
||||
let joinLayout = MutableLiveData<ConferenceDisplayMode>()
|
||||
let joinInProgress = MutableLiveData<Bool>(false)
|
||||
let showLayoutPicker = MutableLiveData<Bool>()
|
||||
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
self.reset()
|
||||
}
|
||||
|
||||
func reset() {
|
||||
joinLayout.value = Core.get().defaultConferenceLayout == .Grid ? .Grid : .ActiveSpeaker
|
||||
joinInProgress.value = false
|
||||
isMicrophoneMuted.value = !micAuthorized()
|
||||
isMuteMicrophoneEnabled.value = true
|
||||
isSpeakerSelected.value = true
|
||||
isVideoEnabled.value = false
|
||||
isVideoAvailable.value = core.videoCaptureEnabled
|
||||
showLayoutPicker.value = false
|
||||
}
|
||||
|
||||
override func toggleMuteMicrophone() {
|
||||
if (!micAuthorized()) {
|
||||
AVAudioSession.sharedInstance().requestRecordPermission { granted in
|
||||
if granted {
|
||||
self.isMicrophoneMuted.value = self.isMicrophoneMuted.value != true
|
||||
}
|
||||
}
|
||||
}
|
||||
self.isMicrophoneMuted.value = self.isMicrophoneMuted.value != true
|
||||
}
|
||||
|
||||
override func toggleSpeaker() {
|
||||
isSpeakerSelected.value = isSpeakerSelected.value != true
|
||||
}
|
||||
|
||||
override func toggleVideo() {
|
||||
isVideoEnabled.value = isVideoEnabled.value != true
|
||||
}
|
||||
|
||||
override func updateUI() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2021 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-android
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
import Foundation
|
||||
import linphonesw
|
||||
|
||||
|
||||
class ScheduledConferencesViewModel {
|
||||
|
||||
var core : Core { get { Core.get() } }
|
||||
static let shared = ScheduledConferencesViewModel()
|
||||
|
||||
var conferences : MutableLiveData<[ScheduledConferenceData]> = MutableLiveData([])
|
||||
var daySplitted : [Date : [ScheduledConferenceData]] = [:]
|
||||
var coreDelegate: CoreDelegateStub?
|
||||
|
||||
init () {
|
||||
|
||||
coreDelegate = CoreDelegateStub(
|
||||
onConferenceInfoReceived: { (core, conferenceInfo) in
|
||||
Log.i("[Scheduled Conferences] New conference info received")
|
||||
self.conferences.value!.append(ScheduledConferenceData(conferenceInfo: conferenceInfo))
|
||||
self.conferences.notifyValue()
|
||||
}
|
||||
|
||||
)
|
||||
|
||||
computeConferenceInfoList()
|
||||
}
|
||||
|
||||
func computeConferenceInfoList() {
|
||||
conferences.value!.removeAll()
|
||||
let now = Date().timeIntervalSince1970 // Linphone uses time_t in seconds
|
||||
let oneHourAgo = now - 3600 // Show all conferences from 1 hour ago and forward
|
||||
core.getConferenceInformationListAfterTime(time: time_t(oneHourAgo)).filter{$0.duration != 0}.forEach { conferenceInfo in
|
||||
conferences.value!.append(ScheduledConferenceData(conferenceInfo: conferenceInfo))
|
||||
}
|
||||
|
||||
daySplitted = [:]
|
||||
conferences.value!.forEach { (conferenceInfo) in
|
||||
let startDateDay = dateAtBeginningOfDay(for: conferenceInfo.rawDate)
|
||||
if (daySplitted[startDateDay] == nil) {
|
||||
daySplitted[startDateDay] = []
|
||||
}
|
||||
daySplitted[startDateDay]!.append(conferenceInfo)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func dateAtBeginningOfDay(for inputDate: Date) -> Date {
|
||||
var calendar = Calendar.current
|
||||
let timeZone = NSTimeZone.system as NSTimeZone
|
||||
calendar.timeZone = timeZone as TimeZone
|
||||
return calendar.date(from: calendar.dateComponents([.year, .month, .day], from: inputDate))!
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
import UIKit
|
||||
import Foundation
|
||||
import linphonesw
|
||||
|
||||
@objc class ConferenceHistoryDetailsView: BackNextNavigationView, UICompositeViewDelegate, UITableViewDataSource {
|
||||
|
||||
|
||||
let participantsListTableView = UITableView()
|
||||
let organizerTableView = UITableView()
|
||||
|
||||
let conectionsListTableView = UITableView()
|
||||
let participantsLabel = StyledLabel(VoipTheme.conference_scheduling_font, " "+VoipTexts.conference_schedule_participants_list)
|
||||
let organiserLabel = StyledLabel(VoipTheme.conference_scheduling_font, " "+VoipTexts.conference_schedule_organizer)
|
||||
|
||||
let datePicker = StyledDatePicker(pickerMode: .date, readOnly:true)
|
||||
let timePicker = StyledDatePicker(pickerMode: .time, readOnly:true)
|
||||
|
||||
var conferenceData : ScheduledConferenceData? {
|
||||
didSet {
|
||||
if let data = conferenceData {
|
||||
super.titleLabel.text = data.subject.value!
|
||||
self.participantsListTableView.reloadData()
|
||||
self.participantsListTableView.removeConstraints().done()
|
||||
self.participantsListTableView.matchParentSideBorders().alignUnder(view: participantsLabel,withMargin: self.form_margin).done()
|
||||
self.participantsListTableView.height(Double(data.conferenceInfo.participants.count) * VoipParticipantCell.cell_height).alignParentBottom().done()
|
||||
datePicker.liveValue = MutableLiveData(conferenceData!.rawDate)
|
||||
timePicker.liveValue = MutableLiveData(conferenceData!.rawDate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static let compositeDescription = UICompositeViewDescription(ConferenceHistoryDetailsView.self, statusBar: StatusBarView.self, tabBar: nil, sideMenu: SideMenuView.self, fullscreen: false, isLeftFragment: false,fragmentWith: nil)
|
||||
static func compositeViewDescription() -> UICompositeViewDescription! { return compositeDescription }
|
||||
func compositeViewDescription() -> UICompositeViewDescription! { return type(of: self).compositeDescription }
|
||||
|
||||
override func viewDidLoad() {
|
||||
|
||||
super.viewDidLoad(
|
||||
backAction: {
|
||||
PhoneMainView.instance().popView(self.compositeViewDescription())
|
||||
},nextAction: {
|
||||
},
|
||||
nextActionEnableCondition: MutableLiveData(false),
|
||||
title:"")
|
||||
super.nextButton.isHidden = true
|
||||
|
||||
|
||||
let schedulingStack = UIStackView()
|
||||
schedulingStack.axis = .vertical
|
||||
contentView.addSubview(schedulingStack)
|
||||
schedulingStack.alignParentTop(withMargin: 2*form_margin).matchParentSideBorders(insetedByDx: form_margin).done()
|
||||
|
||||
let scheduleForm = UIView()
|
||||
schedulingStack.addArrangedSubview(scheduleForm)
|
||||
scheduleForm.matchParentSideBorders().done()
|
||||
|
||||
// Left column (Date)
|
||||
let leftColumn = UIView()
|
||||
scheduleForm.addSubview(leftColumn)
|
||||
leftColumn.matchParentWidthDividedBy(2.2).alignParentLeft(withMargin: form_margin).alignParentTop(withMargin: form_margin).done()
|
||||
|
||||
let dateLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_date)
|
||||
leftColumn.addSubview(dateLabel)
|
||||
dateLabel.alignParentLeft().alignParentTop(withMargin: form_margin).done()
|
||||
|
||||
leftColumn.addSubview(datePicker)
|
||||
datePicker.alignParentLeft().alignUnder(view: dateLabel,withMargin: form_margin).matchParentSideBorders().done()
|
||||
|
||||
leftColumn.wrapContentY().done()
|
||||
|
||||
// Right column (Time)
|
||||
let rightColumn = UIView()
|
||||
scheduleForm.addSubview(rightColumn)
|
||||
rightColumn.matchParentWidthDividedBy(2.2).alignParentRight(withMargin: form_margin).alignParentTop().done()
|
||||
|
||||
let timeLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_time)
|
||||
rightColumn.addSubview(timeLabel)
|
||||
timeLabel.alignParentLeft().alignParentTop(withMargin: form_margin).done()
|
||||
|
||||
rightColumn.addSubview(timePicker)
|
||||
timePicker.alignParentLeft().alignUnder(view: timeLabel,withMargin: form_margin).matchParentSideBorders().done()
|
||||
|
||||
rightColumn.wrapContentY().done()
|
||||
scheduleForm.wrapContentY().done()
|
||||
|
||||
// Organiser
|
||||
|
||||
organiserLabel.backgroundColor = VoipTheme.voipFormBackgroundColor.get()
|
||||
contentView.addSubview(organiserLabel)
|
||||
organiserLabel.matchParentSideBorders().height(form_input_height).alignUnder(view: schedulingStack,withMargin: form_margin*2).done()
|
||||
organiserLabel.textAlignment = .left
|
||||
|
||||
contentView.addSubview(organizerTableView)
|
||||
organizerTableView.isScrollEnabled = false
|
||||
organizerTableView.dataSource = self
|
||||
organizerTableView.register(VoipParticipantCell.self, forCellReuseIdentifier: "VoipParticipantCellSSchedule")
|
||||
organizerTableView.allowsSelection = false
|
||||
if #available(iOS 15.0, *) {
|
||||
organizerTableView.allowsFocus = false
|
||||
}
|
||||
organizerTableView.separatorStyle = .singleLine
|
||||
organizerTableView.separatorColor = VoipTheme.light_grey_color
|
||||
organizerTableView.tag = 1;
|
||||
organizerTableView.matchParentSideBorders().height(VoipParticipantCell.cell_height).alignUnder(view: organiserLabel,withMargin: form_margin).done()
|
||||
|
||||
// Participants
|
||||
participantsLabel.backgroundColor = VoipTheme.voipFormBackgroundColor.get()
|
||||
contentView.addSubview(participantsLabel)
|
||||
participantsLabel.matchParentSideBorders().height(form_input_height).alignUnder(view: organizerTableView,withMargin: form_margin).done()
|
||||
participantsLabel.textAlignment = .left
|
||||
|
||||
contentView.addSubview(participantsListTableView)
|
||||
participantsListTableView.isScrollEnabled = false
|
||||
participantsListTableView.dataSource = self
|
||||
participantsListTableView.register(VoipParticipantCell.self, forCellReuseIdentifier: "VoipParticipantCellSSchedule")
|
||||
participantsListTableView.allowsSelection = false
|
||||
if #available(iOS 15.0, *) {
|
||||
participantsListTableView.allowsFocus = false
|
||||
}
|
||||
participantsListTableView.separatorStyle = .singleLine
|
||||
participantsListTableView.separatorColor = VoipTheme.light_grey_color
|
||||
|
||||
// Goto chat - v2
|
||||
/*
|
||||
let chatButton = FormButton(title: VoipTexts.conference_go_to_chat.uppercased(), backgroundStateColors: VoipTheme.primary_colors_background)
|
||||
contentView.addSubview(chatButton)
|
||||
chatButton.onClick {
|
||||
//let chatRoom = ChatRoom()
|
||||
//PhoneMainView.instance().go(to: chatRoom?.getCobject)
|
||||
}
|
||||
|
||||
chatButton.centerX().alignParentBottom(withMargin: 3*self.form_margin).alignUnder(view: participantsListTableView,withMargin: 3*self.form_margin).done()
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Objc - bridge, as can't access easily to the view model.
|
||||
@objc func setCallLog(callLog:OpaquePointer) {
|
||||
let log = CallLog.getSwiftObject(cObject: callLog)
|
||||
if let conferenceInfo = log.conferenceInfo {
|
||||
self.conferenceData = ScheduledConferenceData(conferenceInfo: conferenceInfo)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TableView datasource delegate
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
guard let data = conferenceData else {
|
||||
return 0
|
||||
}
|
||||
return tableView.tag == 1 ? 1 : data.conferenceInfo.participants.count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell:VoipParticipantCell = tableView.dequeueReusableCell(withIdentifier: "VoipParticipantCellSSchedule") as! VoipParticipantCell
|
||||
guard let data = conferenceData else {
|
||||
return cell
|
||||
}
|
||||
cell.selectionStyle = .none
|
||||
cell.scheduleConfParticipantAddress = tableView.tag == 1 ? data.conferenceInfo.participants.filter {$0.weakEqual(address2: data.conferenceInfo.organizer!)}.first : data.conferenceInfo.participants[indexPath.row]
|
||||
cell.limeBadge.isHidden = true
|
||||
return cell
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,274 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
import UIKit
|
||||
import Foundation
|
||||
import linphonesw
|
||||
import SVProgressHUD
|
||||
|
||||
@objc class ConferenceSchedulingSummaryView: BackNextNavigationView, UICompositeViewDelegate, UITableViewDataSource {
|
||||
|
||||
let CONFERENCE_CREATION_TIME_OUT_SEC = 15.0
|
||||
|
||||
let participantsListTableView = UITableView()
|
||||
|
||||
let datePicker = StyledDatePicker(liveValue: ConferenceSchedulingViewModel.shared.scheduledDate,pickerMode: .date, readOnly:true)
|
||||
let timeZoneValue = StyledValuePicker(liveIndex: ConferenceSchedulingViewModel.shared.scheduledTimeZone,options: ConferenceSchedulingViewModel.timeZones.map({ (tzd: TimeZoneData) -> String in tzd.descWithOffset()}), readOnly:true)
|
||||
let durationValue = StyledValuePicker(liveIndex: ConferenceSchedulingViewModel.shared.scheduledDuration,options: ConferenceSchedulingViewModel.durationList.map({ (duration: Duration) -> String in duration.display}), readOnly:true)
|
||||
let timePicker = StyledDatePicker(liveValue: ConferenceSchedulingViewModel.shared.scheduledTime,pickerMode: .time, readOnly:true)
|
||||
let descriptionInput = StyledTextView(VoipTheme.conference_scheduling_font, placeHolder:VoipTexts.conference_schedule_description_hint,liveValue: ConferenceSchedulingViewModel.shared.description, readOnly:true)
|
||||
let createButton = FormButton(backgroundStateColors: VoipTheme.primary_colors_background)
|
||||
|
||||
|
||||
static let compositeDescription = UICompositeViewDescription(ConferenceSchedulingSummaryView.self, statusBar: StatusBarView.self, tabBar: nil, sideMenu: SideMenuView.self, fullscreen: false, isLeftFragment: false,fragmentWith: nil)
|
||||
static func compositeViewDescription() -> UICompositeViewDescription! { return compositeDescription }
|
||||
func compositeViewDescription() -> UICompositeViewDescription! { return type(of: self).compositeDescription }
|
||||
|
||||
override func viewDidLoad() {
|
||||
|
||||
super.viewDidLoad(
|
||||
backAction: {
|
||||
self.goBackParticipantsListSelection()
|
||||
},nextAction: {
|
||||
},
|
||||
nextActionEnableCondition: ConferenceSchedulingViewModel.shared.continueEnabled,
|
||||
title:VoipTexts.conference_schedule_summary)
|
||||
super.nextButton.isHidden = true
|
||||
|
||||
let subjectLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_subject_title)
|
||||
contentView.addSubview(subjectLabel)
|
||||
subjectLabel.alignParentLeft(withMargin: form_margin).alignParentTop().done()
|
||||
|
||||
let encryptedIcon = UIImageView(image: UIImage(named: "security_2_indicator"))
|
||||
encryptedIcon.contentMode = .scaleAspectFit
|
||||
contentView.addSubview(encryptedIcon)
|
||||
encryptedIcon.height(form_input_height).alignParentTop().alignParentTop().alignParentRight(withMargin: form_margin).alignHorizontalCenterWith(subjectLabel).done()
|
||||
ConferenceSchedulingViewModel.shared.isEncrypted.readCurrentAndObserve { (encrypt) in
|
||||
encryptedIcon.isHidden = encrypt != true
|
||||
}
|
||||
|
||||
|
||||
let subjectInput = StyledTextView(VoipTheme.conference_scheduling_font, placeHolder:VoipTexts.conference_schedule_subject_hint, liveValue: ConferenceSchedulingViewModel.shared.subject, readOnly:true)
|
||||
contentView.addSubview(subjectInput)
|
||||
subjectInput.alignUnder(view: subjectLabel,withMargin: form_margin).matchParentSideBorders(insetedByDx: form_margin).height(form_input_height).done()
|
||||
|
||||
|
||||
let schedulingStack = UIStackView()
|
||||
schedulingStack.axis = .vertical
|
||||
contentView.addSubview(schedulingStack)
|
||||
schedulingStack.alignUnder(view: subjectInput,withMargin: 3*form_margin).matchParentSideBorders(insetedByDx: form_margin).done()
|
||||
|
||||
|
||||
let scheduleForm = UIView()
|
||||
schedulingStack.addArrangedSubview(scheduleForm)
|
||||
scheduleForm.matchParentSideBorders().done()
|
||||
ConferenceSchedulingViewModel.shared.scheduleForLater.readCurrentAndObserve { (forLater) in scheduleForm.isHidden = forLater != true }
|
||||
|
||||
// Left column (Date & Time)
|
||||
let leftColumn = UIView()
|
||||
scheduleForm.addSubview(leftColumn)
|
||||
leftColumn.matchParentWidthDividedBy(2.2).alignParentLeft(withMargin: form_margin).alignParentTop(withMargin: form_margin).done()
|
||||
|
||||
let dateLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_date)
|
||||
leftColumn.addSubview(dateLabel)
|
||||
dateLabel.alignParentLeft().alignParentTop(withMargin: form_margin).done()
|
||||
|
||||
leftColumn.addSubview(datePicker)
|
||||
datePicker.alignParentLeft().alignUnder(view: dateLabel,withMargin: form_margin).matchParentSideBorders().done()
|
||||
|
||||
let timeLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_time)
|
||||
leftColumn.addSubview(timeLabel)
|
||||
timeLabel.alignParentLeft().alignUnder(view: datePicker,withMargin: form_margin).done()
|
||||
|
||||
leftColumn.addSubview(timePicker)
|
||||
timePicker.alignParentLeft().alignUnder(view: timeLabel,withMargin: form_margin).matchParentSideBorders().done()
|
||||
|
||||
leftColumn.wrapContentY().done()
|
||||
|
||||
|
||||
// Right column (Duration & Timezone)
|
||||
let rightColumn = UIView()
|
||||
scheduleForm.addSubview(rightColumn)
|
||||
rightColumn.matchParentWidthDividedBy(2.2).alignParentRight(withMargin: form_margin).alignParentTop().done()
|
||||
|
||||
let durationLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_duration)
|
||||
rightColumn.addSubview(durationLabel)
|
||||
durationLabel.alignParentLeft().alignParentTop(withMargin: form_margin).done()
|
||||
|
||||
rightColumn.addSubview(durationValue)
|
||||
durationValue.alignParentLeft().alignUnder(view: durationLabel,withMargin: form_margin).matchParentSideBorders().done()
|
||||
|
||||
let timeZoneLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_timezone)
|
||||
rightColumn.addSubview(timeZoneLabel)
|
||||
timeZoneLabel.alignParentLeft().alignUnder(view: durationValue,withMargin: form_margin).done()
|
||||
|
||||
rightColumn.addSubview(timeZoneValue)
|
||||
timeZoneValue.alignParentLeft().alignUnder(view: timeZoneLabel,withMargin: form_margin).matchParentSideBorders().done()
|
||||
|
||||
rightColumn.wrapContentY().done()
|
||||
|
||||
// Description
|
||||
let descriptionLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_description_title)
|
||||
scheduleForm.addSubview(descriptionLabel)
|
||||
descriptionLabel.alignUnder(view: leftColumn,withMargin: form_margin).alignUnder(view: rightColumn,withMargin: form_margin).matchParentSideBorders(insetedByDx: form_margin).done()
|
||||
|
||||
descriptionInput.textContainer.maximumNumberOfLines = 5
|
||||
descriptionInput.textContainer.lineBreakMode = .byWordWrapping
|
||||
scheduleForm.addSubview(descriptionInput)
|
||||
descriptionInput.alignUnder(view: descriptionLabel,withMargin: form_margin).matchParentSideBorders(insetedByDx: form_margin).height(description_height).alignParentBottom(withMargin: form_margin*2).done()
|
||||
|
||||
scheduleForm.wrapContentY().done()
|
||||
|
||||
// Sending method
|
||||
let viaChatLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_send_invite_chat_summary)
|
||||
contentView.addSubview(viaChatLabel)
|
||||
viaChatLabel.matchParentSideBorders(insetedByDx: form_margin).alignUnder(view: schedulingStack,withMargin: 2*form_margin).done()
|
||||
ConferenceSchedulingViewModel.shared.sendInviteViaChat.readCurrentAndObserve { (sendChat) in
|
||||
viaChatLabel.isHidden = sendChat != true || ConferenceSchedulingViewModel.shared.scheduleForLater.value != true
|
||||
}
|
||||
|
||||
// Participants
|
||||
let participantsLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_participants_list)
|
||||
participantsLabel.backgroundColor = VoipTheme.voipFormBackgroundColor.get()
|
||||
contentView.addSubview(participantsLabel)
|
||||
participantsLabel.matchParentSideBorders().height(form_input_height).alignUnder(view: viaChatLabel,withMargin: form_margin*2).done()
|
||||
participantsLabel.textAlignment = .center
|
||||
|
||||
|
||||
contentView.addSubview(participantsListTableView)
|
||||
participantsListTableView.isScrollEnabled = false
|
||||
participantsListTableView.dataSource = self
|
||||
participantsListTableView.register(VoipParticipantCell.self, forCellReuseIdentifier: "VoipParticipantCellSSchedule")
|
||||
participantsListTableView.allowsSelection = false
|
||||
if #available(iOS 15.0, *) {
|
||||
participantsListTableView.allowsFocus = false
|
||||
}
|
||||
participantsListTableView.separatorStyle = .singleLine
|
||||
participantsListTableView.separatorColor = VoipTheme.light_grey_color
|
||||
|
||||
ConferenceSchedulingViewModel.shared.selectedAddresses.readCurrentAndObserve { (addresses) in
|
||||
self.participantsListTableView.reloadData()
|
||||
self.participantsListTableView.removeConstraints().done()
|
||||
self.participantsListTableView.matchParentSideBorders().alignUnder(view: participantsLabel,withMargin: self.form_margin).done()
|
||||
self.participantsListTableView.height(Double(addresses!.count) * VoipParticipantCell.cell_height).done()
|
||||
}
|
||||
|
||||
// Create / Schedule
|
||||
contentView.addSubview(createButton)
|
||||
ConferenceSchedulingViewModel.shared.scheduleForLater.readCurrentAndObserve { _ in
|
||||
self.createButton.title = ConferenceSchedulingViewModel.shared.scheduleForLater.value == true ? VoipTexts.conference_schedule_start.uppercased() : VoipTexts.conference_group_call_create.uppercased()
|
||||
self.createButton.addSidePadding()
|
||||
}
|
||||
|
||||
ConferenceSchedulingViewModel.shared.conferenceCreationInProgress.observe { progress in
|
||||
if (progress == true) {
|
||||
SVProgressHUD.show()
|
||||
} else {
|
||||
SVProgressHUD.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
var enableCreationTimeOut = false
|
||||
|
||||
ConferenceSchedulingViewModel.shared.conferenceCreationCompletedEvent.observe { pair in
|
||||
enableCreationTimeOut = false
|
||||
if (ConferenceSchedulingViewModel.shared.scheduleForLater.value == true) {
|
||||
PhoneMainView.instance().pop(toView:ScheduledConferencesView.compositeDescription)
|
||||
VoipDialog.toast(message: VoipTexts.conference_schedule_info_created)
|
||||
|
||||
}
|
||||
}
|
||||
ConferenceSchedulingViewModel.shared.onErrorEvent.observe { error in
|
||||
VoipDialog.init(message: error!).show()
|
||||
}
|
||||
createButton.onClick {
|
||||
enableCreationTimeOut = true
|
||||
ConferenceSchedulingViewModel.shared.createConference()
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + self.CONFERENCE_CREATION_TIME_OUT_SEC) {
|
||||
if (enableCreationTimeOut) {
|
||||
enableCreationTimeOut = false
|
||||
ConferenceSchedulingViewModel.shared.conferenceCreationInProgress.value = false
|
||||
ConferenceSchedulingViewModel.shared.onErrorEvent.value = VoipTexts.call_error_server_timeout
|
||||
}
|
||||
}
|
||||
}
|
||||
ConferenceSchedulingViewModel.shared.scheduleForLater.readCurrentAndObserve { (later) in
|
||||
self.createButton.title = ConferenceSchedulingViewModel.shared.scheduleForLater.value == true ? VoipTexts.conference_schedule_start.uppercased() : VoipTexts.conference_group_call_create.uppercased()
|
||||
viaChatLabel.isHidden = later != true || ConferenceSchedulingViewModel.shared.sendInviteViaChat.value != true
|
||||
self.createButton.addSidePadding()
|
||||
}
|
||||
|
||||
createButton.centerX().alignParentBottom(withMargin: 3*self.form_margin).alignUnder(view: participantsListTableView,withMargin: 3*self.form_margin).done()
|
||||
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
datePicker.liveValue = ConferenceSchedulingViewModel.shared.scheduledDate
|
||||
timeZoneValue.setIndex(index: ConferenceSchedulingViewModel.shared.scheduledTimeZone.value!)
|
||||
durationValue.setIndex(index: ConferenceSchedulingViewModel.shared.scheduledDuration.value!)
|
||||
timePicker.liveValue = ConferenceSchedulingViewModel.shared.scheduledTime
|
||||
descriptionInput.text = ConferenceSchedulingViewModel.shared.description.value
|
||||
createButton.addSidePadding()
|
||||
super.viewWillAppear(animated)
|
||||
}
|
||||
|
||||
|
||||
|
||||
func goBackParticipantsListSelection() {
|
||||
let view: ChatConversationCreateView = VIEW(ChatConversationCreateView.compositeViewDescription())
|
||||
let addresses = ConferenceSchedulingViewModel.shared.selectedAddresses.value!.map { (address) in String(address.asStringUriOnly()) }
|
||||
view.tableController.contactsGroup = (addresses as NSArray).mutableCopy() as? NSMutableArray
|
||||
view.tableController.notFirstTime = true
|
||||
view.isForEditing = false
|
||||
view.isForVoipConference = true
|
||||
PhoneMainView.instance().pop(toView: view.compositeViewDescription())
|
||||
}
|
||||
|
||||
// Objc - bridge, as can't access easily to the view model.
|
||||
@objc func setParticipants(addresses:[String]) {
|
||||
ConferenceSchedulingViewModel.shared.selectedAddresses.value = []
|
||||
return addresses.forEach { (address) in
|
||||
if let address = try?Factory.Instance.createAddress(addr: address) {
|
||||
ConferenceSchedulingViewModel.shared.selectedAddresses.value?.append(address)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TableView datasource delegate
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
guard let participants = ConferenceSchedulingViewModel.shared.selectedAddresses.value else {
|
||||
return 0
|
||||
}
|
||||
return participants.count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell:VoipParticipantCell = tableView.dequeueReusableCell(withIdentifier: "VoipParticipantCellSSchedule") as! VoipParticipantCell
|
||||
guard let participant = ConferenceSchedulingViewModel.shared.selectedAddresses.value?[indexPath.row] else {
|
||||
return cell
|
||||
}
|
||||
cell.selectionStyle = .none
|
||||
cell.scheduleConfParticipantAddress = participant
|
||||
cell.limeBadge.isHidden = ConferenceSchedulingViewModel.shared.isEncrypted.value != true
|
||||
return cell
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
227
Classes/Swift/Conference/Views/ConferenceSchedulingView.swift
Normal file
227
Classes/Swift/Conference/Views/ConferenceSchedulingView.swift
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
import UIKit
|
||||
import Foundation
|
||||
import linphonesw
|
||||
|
||||
@objc class ConferenceSchedulingView: BackNextNavigationView, UICompositeViewDelegate {
|
||||
|
||||
|
||||
static let compositeDescription = UICompositeViewDescription(ConferenceSchedulingView.self, statusBar: StatusBarView.self, tabBar: nil, sideMenu: SideMenuView.self, fullscreen: false, isLeftFragment: false,fragmentWith: nil)
|
||||
static func compositeViewDescription() -> UICompositeViewDescription! { return compositeDescription }
|
||||
func compositeViewDescription() -> UICompositeViewDescription! { return type(of: self).compositeDescription }
|
||||
|
||||
let datePicker = StyledDatePicker(liveValue: ConferenceSchedulingViewModel.shared.scheduledDate,pickerMode: .date)
|
||||
let timeZoneValue = StyledValuePicker(liveIndex: ConferenceSchedulingViewModel.shared.scheduledTimeZone,options: ConferenceSchedulingViewModel.timeZones.map({ (tzd: TimeZoneData) -> String in tzd.descWithOffset()}))
|
||||
let durationValue = StyledValuePicker(liveIndex: ConferenceSchedulingViewModel.shared.scheduledDuration,options: ConferenceSchedulingViewModel.durationList.map({ (duration: Duration) -> String in duration.display}))
|
||||
let timePicker = StyledDatePicker(liveValue: ConferenceSchedulingViewModel.shared.scheduledTime,pickerMode: .time)
|
||||
let descriptionInput = StyledTextView(VoipTheme.conference_scheduling_font, placeHolder:VoipTexts.conference_schedule_description_hint,liveValue: ConferenceSchedulingViewModel.shared.description)
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
|
||||
super.viewDidLoad(
|
||||
backAction: {
|
||||
PhoneMainView.instance().popView(self.compositeViewDescription())
|
||||
},nextAction: {
|
||||
self.gotoParticipantsListSelection()
|
||||
},
|
||||
nextActionEnableCondition: ConferenceSchedulingViewModel.shared.continueEnabled,
|
||||
title:VoipTexts.conference_group_call_title)
|
||||
|
||||
let subjectLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_subject_title)
|
||||
subjectLabel.addIndicatorIcon(iconName: "voip_mandatory")
|
||||
contentView.addSubview(subjectLabel)
|
||||
subjectLabel.alignParentLeft(withMargin: form_margin).alignParentTop().done()
|
||||
|
||||
let subjectInput = StyledTextView(VoipTheme.conference_scheduling_font, placeHolder:VoipTexts.conference_schedule_subject_hint, liveValue: ConferenceSchedulingViewModel.shared.subject,maxLines:1)
|
||||
contentView.addSubview(subjectInput)
|
||||
subjectInput.alignUnder(view: subjectLabel,withMargin: form_margin).matchParentSideBorders(insetedByDx: form_margin).height(form_input_height).done()
|
||||
|
||||
let schedulingStack = UIStackView()
|
||||
schedulingStack.axis = .vertical
|
||||
schedulingStack.backgroundColor = VoipTheme.voipFormBackgroundColor.get()
|
||||
contentView.addSubview(schedulingStack)
|
||||
schedulingStack.alignUnder(view: subjectInput,withMargin: form_margin).matchParentSideBorders(insetedByDx: form_margin).done()
|
||||
|
||||
let scheduleForLater = UIView()
|
||||
schedulingStack.addArrangedSubview(scheduleForLater)
|
||||
scheduleForLater.matchParentSideBorders().height(schdule_for_later_height).done()
|
||||
|
||||
let laterSwitch = StyledSwitch(liveValue: ConferenceSchedulingViewModel.shared.scheduleForLater)
|
||||
scheduleForLater.addSubview(laterSwitch)
|
||||
laterSwitch.alignParentTop(withMargin: form_margin*2.5).alignParentLeft(withMargin: form_margin).centerY().done()
|
||||
|
||||
let laterLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_later)
|
||||
laterLabel.numberOfLines = 2
|
||||
scheduleForLater.addSubview(laterLabel)
|
||||
laterLabel.alignParentTop(withMargin: form_margin*2).toRightOf(laterSwitch, withLeftMargin: form_margin*2).alignParentRight(withMargin: form_margin).done()
|
||||
|
||||
let scheduleForm = UIView()
|
||||
schedulingStack.addArrangedSubview(scheduleForm)
|
||||
scheduleForm.matchParentSideBorders().done()
|
||||
|
||||
// Left column (Date & Time)
|
||||
let leftColumn = UIView()
|
||||
scheduleForm.addSubview(leftColumn)
|
||||
leftColumn.matchParentWidthDividedBy(2.2).alignParentLeft(withMargin: form_margin).alignParentTop(withMargin: form_margin).done()
|
||||
|
||||
let dateLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_date)
|
||||
dateLabel.addIndicatorIcon(iconName: "voip_mandatory")
|
||||
leftColumn.addSubview(dateLabel)
|
||||
dateLabel.alignParentLeft().alignParentTop(withMargin: form_margin).done()
|
||||
|
||||
leftColumn.addSubview(datePicker)
|
||||
datePicker.alignParentLeft().alignUnder(view: dateLabel,withMargin: form_margin).matchParentSideBorders().done()
|
||||
|
||||
let timeLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_time)
|
||||
timeLabel.addIndicatorIcon(iconName: "voip_mandatory")
|
||||
leftColumn.addSubview(timeLabel)
|
||||
timeLabel.alignParentLeft().alignUnder(view: datePicker,withMargin: form_margin).done()
|
||||
|
||||
leftColumn.addSubview(timePicker)
|
||||
timePicker.alignParentLeft().alignUnder(view: timeLabel,withMargin: form_margin).matchParentSideBorders().done()
|
||||
|
||||
leftColumn.wrapContentY().done()
|
||||
|
||||
|
||||
// Right column (Duration & Timezone)
|
||||
let rightColumn = UIView()
|
||||
scheduleForm.addSubview(rightColumn)
|
||||
rightColumn.matchParentWidthDividedBy(2.2).alignParentRight(withMargin: form_margin).alignParentTop().done()
|
||||
|
||||
let durationLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_duration)
|
||||
rightColumn.addSubview(durationLabel)
|
||||
durationLabel.alignParentLeft().alignParentTop(withMargin: form_margin).done()
|
||||
|
||||
rightColumn.addSubview(durationValue)
|
||||
durationValue.alignParentLeft().alignUnder(view: durationLabel,withMargin: form_margin).matchParentSideBorders().done()
|
||||
|
||||
let timeZoneLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_timezone)
|
||||
rightColumn.addSubview(timeZoneLabel)
|
||||
timeZoneLabel.alignParentLeft().alignUnder(view: durationValue,withMargin: form_margin).done()
|
||||
|
||||
rightColumn.addSubview(timeZoneValue)
|
||||
timeZoneValue.alignParentLeft().alignUnder(view: timeZoneLabel,withMargin: form_margin).matchParentSideBorders().done()
|
||||
|
||||
rightColumn.wrapContentY().done()
|
||||
|
||||
// Description
|
||||
let descriptionLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_description_title)
|
||||
scheduleForm.addSubview(descriptionLabel)
|
||||
descriptionLabel.alignUnder(view: leftColumn,withMargin: form_margin).alignUnder(view: rightColumn,withMargin: form_margin).matchParentSideBorders(insetedByDx: form_margin).done()
|
||||
|
||||
descriptionInput.textContainer.maximumNumberOfLines = 5
|
||||
descriptionInput.textContainer.lineBreakMode = .byWordWrapping
|
||||
scheduleForm.addSubview(descriptionInput)
|
||||
descriptionInput.alignUnder(view: descriptionLabel,withMargin: form_margin).matchParentSideBorders(insetedByDx: form_margin).height(description_height).alignParentBottom(withMargin: form_margin*2).done()
|
||||
|
||||
scheduleForm.wrapContentY().done()
|
||||
|
||||
// Sending methods
|
||||
let viaChatSwitch = StyledCheckBox(liveValue: ConferenceSchedulingViewModel.shared.sendInviteViaChat)
|
||||
contentView.addSubview(viaChatSwitch)
|
||||
viaChatSwitch.alignParentLeft(withMargin: form_margin).alignUnder(view: schedulingStack,withMargin: 2*form_margin).done()
|
||||
|
||||
let viaChatLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_send_invite_chat)
|
||||
contentView.addSubview(viaChatLabel)
|
||||
viaChatLabel.toRightOf(viaChatSwitch,withLeftMargin: form_margin).alignUnder(view: schedulingStack,withMargin: 2*form_margin).alignHorizontalCenterWith(viaChatSwitch).done()
|
||||
|
||||
/* Hidden as in Android 9.6.2022
|
||||
|
||||
let viaMailSwitch = StyledCheckBox(liveValue: ConferenceSchedulingViewModel.shared.sendInviteViaEmail)
|
||||
contentView.addSubview(viaMailSwitch)
|
||||
viaMailSwitch.alignParentLeft(withMargin: form_margin).alignUnder(view: viaChatSwitch,withMargin: 2*form_margin).done()
|
||||
|
||||
let viaMailLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_send_invite_email)
|
||||
contentView.addSubview(viaMailLabel)
|
||||
viaMailLabel.toRightOf(viaMailSwitch,withLeftMargin: form_margin).alignUnder(view: viaChatLabel,withMargin: 2*form_margin).alignHorizontalCenterWith(viaMailSwitch).done()
|
||||
|
||||
// Encryption
|
||||
let encryptLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_encryption)
|
||||
contentView.addSubview(encryptLabel)
|
||||
encryptLabel.alignUnder(view: viaMailLabel,withMargin: 4*form_margin).centerX().done()
|
||||
|
||||
let encryptCombo = UIStackView()
|
||||
contentView.addSubview(encryptCombo)
|
||||
encryptCombo.alignUnder(view: encryptLabel,withMargin: form_margin).centerX().height(form_input_height).done()
|
||||
|
||||
let unencryptedIcon = UIImageView(image: UIImage(named: "security_toggle_icon_grey"))
|
||||
unencryptedIcon.contentMode = .scaleAspectFit
|
||||
encryptCombo.addArrangedSubview(unencryptedIcon)
|
||||
|
||||
let encryptSwitch = StyledSwitch(liveValue: ConferenceSchedulingViewModel.shared.isEncrypted)
|
||||
encryptCombo.addArrangedSubview(encryptSwitch)
|
||||
encryptSwitch.centerY().alignParentTop(withMargin: form_margin).done()
|
||||
|
||||
let encryptedIcon = UIImageView(image: UIImage(named: "security_toggle_icon_green"))
|
||||
encryptedIcon.contentMode = .scaleAspectFit
|
||||
encryptCombo.addArrangedSubview(encryptedIcon)
|
||||
|
||||
*/
|
||||
|
||||
// Mandatory label
|
||||
|
||||
let mandatoryLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_mandatory_field)
|
||||
mandatoryLabel.addIndicatorIcon(iconName: "voip_mandatory", trailing: false)
|
||||
contentView.addSubview(mandatoryLabel)
|
||||
mandatoryLabel.alignUnder(view: viaChatSwitch,withMargin: 2*form_margin).centerX().matchParentSideBorders().done()
|
||||
mandatoryLabel.textAlignment = .center
|
||||
|
||||
mandatoryLabel.alignParentBottom().done()
|
||||
|
||||
// Schedule for later observer
|
||||
ConferenceSchedulingViewModel.shared.scheduleForLater.readCurrentAndObserve { (forLater) in
|
||||
scheduleForm.isHidden = forLater != true
|
||||
super.titleLabel.text = forLater == true ? VoipTexts.conference_schedule_title : VoipTexts.conference_group_call_title
|
||||
viaChatSwitch.isHidden = forLater != true
|
||||
viaChatLabel.isHidden = forLater != true
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
datePicker.liveValue = ConferenceSchedulingViewModel.shared.scheduledDate
|
||||
timeZoneValue.setIndex(index: ConferenceSchedulingViewModel.shared.scheduledTimeZone.value!)
|
||||
durationValue.setIndex(index: ConferenceSchedulingViewModel.shared.scheduledDuration.value!)
|
||||
timePicker.liveValue = ConferenceSchedulingViewModel.shared.scheduledTime
|
||||
descriptionInput.text = ConferenceSchedulingViewModel.shared.description.value
|
||||
}
|
||||
|
||||
func gotoParticipantsListSelection() {
|
||||
let view: ChatConversationCreateView = self.VIEW(ChatConversationCreateView.compositeViewDescription());
|
||||
let addresses = ConferenceSchedulingViewModel.shared.selectedAddresses.value!.map { (address) in String(address.asStringUriOnly()) }
|
||||
view.tableController.contactsGroup = (addresses as NSArray).mutableCopy() as? NSMutableArray
|
||||
view.isForEditing = false
|
||||
view.isForVoipConference = true
|
||||
view.isForOngoingVoipConference = false
|
||||
view.tableController.notFirstTime = true
|
||||
view.isGroupChat = true
|
||||
PhoneMainView.instance().changeCurrentView(view.compositeViewDescription())
|
||||
}
|
||||
|
||||
@objc func resetViewModel() {
|
||||
ConferenceSchedulingViewModel.shared.reset()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,209 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
import UIKit
|
||||
import linphonesw
|
||||
|
||||
|
||||
@objc class ConferenceWaitingRoomFragment: UIViewController, UICompositeViewDelegate { // Replaces CallView
|
||||
|
||||
// Layout constants
|
||||
let common_margin = 17.0
|
||||
let switch_camera_button_size = 50
|
||||
let switch_camera_button_margins = 7.0
|
||||
let content_inset = 12.0
|
||||
let button_spacing = 15.0
|
||||
let center_view_corner_radius = 20.0
|
||||
let button_width = 150
|
||||
|
||||
|
||||
var audioRoutesView : AudioRoutesView? = nil
|
||||
let subject = StyledLabel(VoipTheme.conference_preview_subject_font)
|
||||
let localVideo = UIView()
|
||||
let switchCamera = UIImageView(image: UIImage(named:"voip_change_camera")?.tinted(with:.white))
|
||||
let noVideoLabel = StyledLabel(VoipTheme.conference_waiting_room_no_video_font, VoipTexts.conference_waiting_room_video_disabled)
|
||||
|
||||
let buttonsView = UIStackView()
|
||||
let cancel = FormButton(title: VoipTexts.cancel.uppercased(), backgroundStateColors: VoipTheme.primary_colors_background_gray, bold:false)
|
||||
let start = FormButton(title: VoipTexts.conference_waiting_room_start_call.uppercased(), backgroundStateColors: VoipTheme.primary_colors_background)
|
||||
let conferenceJoinSpinner = RotatingSpinner()
|
||||
|
||||
var conferenceUrl : String? = nil
|
||||
let conferenceSubject = MutableLiveData<String>()
|
||||
|
||||
static let compositeDescription = UICompositeViewDescription(ConferenceWaitingRoomFragment.self, statusBar: StatusBarView.self, tabBar: nil, sideMenu: nil, fullscreen: false, isLeftFragment: false,fragmentWith: nil)
|
||||
static func compositeViewDescription() -> UICompositeViewDescription! { return compositeDescription }
|
||||
func compositeViewDescription() -> UICompositeViewDescription! { return type(of: self).compositeDescription }
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
view.backgroundColor = VoipTheme.voipBackgroundColor.get()
|
||||
|
||||
view.addSubview(subject)
|
||||
subject.centerX().alignParentTop(withMargin: common_margin).done()
|
||||
conferenceSubject.observe { subject in
|
||||
self.subject.text = subject
|
||||
}
|
||||
|
||||
// Controls
|
||||
let controlsView = ControlsView(showVideo: true, controlsViewModel: ConferenceWaitingRoomViewModel.sharedModel)
|
||||
view.addSubview(controlsView)
|
||||
controlsView.alignParentBottom(withMargin:SharedLayoutConstants.buttons_bottom_margin).centerX().done()
|
||||
|
||||
// Layoout picker
|
||||
let layoutPicker = CallControlButton(imageInset : UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8),buttonTheme: VoipTheme.conf_waiting_room_layout_picker, onClickAction: {
|
||||
ConferenceWaitingRoomViewModel.sharedModel.showLayoutPicker.value = ConferenceWaitingRoomViewModel.sharedModel.showLayoutPicker.value != true
|
||||
})
|
||||
view.addSubview(layoutPicker)
|
||||
layoutPicker.alignParentBottom(withMargin:SharedLayoutConstants.buttons_bottom_margin).alignParentRight(withMargin:SharedLayoutConstants.buttons_bottom_margin).done()
|
||||
|
||||
ConferenceWaitingRoomViewModel.sharedModel.joinLayout.readCurrentAndObserve { layout in
|
||||
var icon = ""
|
||||
switch (layout!) {
|
||||
case .Grid: icon = "voip_conference_mosaic"; break
|
||||
case .ActiveSpeaker: icon = "voip_conference_active_speaker"; break
|
||||
case .AudioOnly:
|
||||
icon = "voip_conference_audio_only"
|
||||
ConferenceWaitingRoomViewModel.sharedModel.isVideoEnabled.value = false
|
||||
break
|
||||
}
|
||||
layoutPicker.applyTintedIcons(tintedIcons: [UIButton.State.normal.rawValue : TintableIcon(name: icon ,tintColor: LightDarkColor(.white,.white))])
|
||||
}
|
||||
|
||||
let layoutPickerView = ConferenceLayoutPickerView()
|
||||
view.addSubview(layoutPickerView)
|
||||
layoutPickerView.alignAbove(view:layoutPicker,withMargin:button_spacing).alignVerticalCenterWith(layoutPicker).done()
|
||||
|
||||
ConferenceWaitingRoomViewModel.sharedModel.showLayoutPicker.readCurrentAndObserve { show in
|
||||
layoutPicker.isSelected = show == true
|
||||
layoutPickerView.isHidden = show != true
|
||||
if (show == true) {
|
||||
self.view.bringSubviewToFront(layoutPickerView)
|
||||
}
|
||||
}
|
||||
|
||||
// Form buttons
|
||||
buttonsView.axis = .horizontal
|
||||
buttonsView.spacing = button_spacing
|
||||
view.addSubview(buttonsView)
|
||||
buttonsView.alignAbove(view: controlsView,withMargin: SharedLayoutConstants.buttons_bottom_margin).centerX().done()
|
||||
|
||||
start.width(button_width).done()
|
||||
cancel.width(button_width).done()
|
||||
|
||||
buttonsView.addArrangedSubview(cancel)
|
||||
buttonsView.addArrangedSubview(start)
|
||||
|
||||
cancel.onClick {
|
||||
Core.get().calls.forEach { call in
|
||||
if ([Call.State.OutgoingInit, Call.State.OutgoingRinging, Call.State.OutgoingProgress].contains(call.state)) {
|
||||
CallManager.instance().terminateCall(call: call.getCobject)
|
||||
}
|
||||
}
|
||||
ConferenceWaitingRoomViewModel.sharedModel.joinInProgress.value = false
|
||||
PhoneMainView.instance().popView(self.compositeViewDescription())
|
||||
}
|
||||
|
||||
start.onClick {
|
||||
ConferenceWaitingRoomViewModel.sharedModel.joinInProgress.value = true
|
||||
self.conferenceUrl.map{ CallManager.instance().startCall(addr: $0, isSas: false, isVideo: ConferenceWaitingRoomViewModel.sharedModel.isVideoEnabled.value!, isConference: true) }
|
||||
}
|
||||
|
||||
ConferenceWaitingRoomViewModel.sharedModel.joinInProgress.readCurrentAndObserve { joining in
|
||||
self.start.isEnabled = joining != true
|
||||
//self.localVideo.isHidden = joining == true (UX question as video window goes black by the core, better black or hidden ?)
|
||||
self.noVideoLabel.isHidden = joining == true
|
||||
layoutPicker.isHidden = joining == true
|
||||
if (joining == true) {
|
||||
self.view.addSubview(self.conferenceJoinSpinner)
|
||||
self.conferenceJoinSpinner.square(IncomingOutgoingCommonView.spinner_size).center().done()
|
||||
self.conferenceJoinSpinner.startRotation()
|
||||
controlsView.isHidden = true
|
||||
} else {
|
||||
self.conferenceJoinSpinner.stopRotation()
|
||||
self.conferenceJoinSpinner.removeFromSuperview()
|
||||
controlsView.isHidden = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// localVideo view
|
||||
localVideo.layer.cornerRadius = center_view_corner_radius
|
||||
localVideo.clipsToBounds = true
|
||||
localVideo.contentMode = .scaleAspectFill
|
||||
localVideo.backgroundColor = .black
|
||||
self.view.addSubview(localVideo)
|
||||
localVideo.matchParentSideBorders(insetedByDx: content_inset).alignAbove(view:buttonsView,withMargin:SharedLayoutConstants.buttons_bottom_margin).alignUnder(view: subject,withMargin: common_margin).done()
|
||||
localVideo.addSubview(switchCamera)
|
||||
switchCamera.alignParentTop(withMargin: switch_camera_button_margins).alignParentRight(withMargin: switch_camera_button_margins).square(switch_camera_button_size).done()
|
||||
switchCamera.contentMode = .scaleAspectFit
|
||||
switchCamera.onClick {
|
||||
Core.get().videoPreviewEnabled = false
|
||||
Core.get().toggleCamera()
|
||||
Core.get().nativePreviewWindow = self.localVideo
|
||||
Core.get().videoPreviewEnabled = true
|
||||
}
|
||||
|
||||
self.view.addSubview(noVideoLabel)
|
||||
noVideoLabel.center().done()
|
||||
|
||||
ConferenceWaitingRoomViewModel.sharedModel.isVideoEnabled.readCurrentAndObserve { videoEnabled in
|
||||
Core.get().videoPreviewEnabled = videoEnabled == true
|
||||
self.localVideo.isHidden = videoEnabled != true
|
||||
self.switchCamera.isHidden = videoEnabled != true
|
||||
self.noVideoLabel.isHidden = videoEnabled == true
|
||||
}
|
||||
|
||||
|
||||
// Audio Routes
|
||||
audioRoutesView = AudioRoutesView()
|
||||
view.addSubview(audioRoutesView!)
|
||||
audioRoutesView!.alignBottomWith(otherView: controlsView).done()
|
||||
ConferenceWaitingRoomViewModel.sharedModel.audioRoutesSelected.readCurrentAndObserve { (audioRoutesSelected) in
|
||||
self.audioRoutesView!.isHidden = audioRoutesSelected != true
|
||||
}
|
||||
audioRoutesView!.alignAbove(view:controlsView,withMargin:SharedLayoutConstants.buttons_bottom_margin).centerX().done()
|
||||
|
||||
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(true)
|
||||
ConferenceWaitingRoomViewModel.sharedModel.audioRoutesSelected.value = false
|
||||
ConferenceWaitingRoomViewModel.sharedModel.reset()
|
||||
Core.get().nativePreviewWindow = localVideo
|
||||
Core.get().videoPreviewEnabled = ConferenceWaitingRoomViewModel.sharedModel.isVideoEnabled.value == true
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
ControlsViewModel.shared.fullScreenMode.value = false
|
||||
Core.get().nativePreviewWindow = nil
|
||||
Core.get().videoPreviewEnabled = false
|
||||
ConferenceWaitingRoomViewModel.sharedModel.joinInProgress.value = false
|
||||
super.viewWillDisappear(animated)
|
||||
}
|
||||
|
||||
@objc func setDetails(subject:String, url:String) {
|
||||
self.conferenceSubject.value = subject
|
||||
self.conferenceUrl = url
|
||||
}
|
||||
|
||||
}
|
||||
138
Classes/Swift/Conference/Views/ICSBubbleView.swift
Normal file
138
Classes/Swift/Conference/Views/ICSBubbleView.swift
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
import Foundation
|
||||
import linphonesw
|
||||
|
||||
@objc class ICSBubbleView: UIView {
|
||||
|
||||
let corner_radius = 7.0
|
||||
let border_width = 2.0
|
||||
let rows_spacing = 6.0
|
||||
let inner_padding = 8.0
|
||||
let indicator_y = 3.0
|
||||
let share_size = 25
|
||||
let join_share_width = 150.0
|
||||
|
||||
|
||||
let inviteTitle = StyledLabel(VoipTheme.conference_invite_title_font, VoipTexts.conference_invite_title)
|
||||
let subject = StyledLabel(VoipTheme.conference_invite_subject_font)
|
||||
let participants = StyledLabel(VoipTheme.conference_invite_desc_font)
|
||||
let date = StyledLabel(VoipTheme.conference_invite_desc_font)
|
||||
let timeDuration = StyledLabel(VoipTheme.conference_invite_desc_font)
|
||||
let descriptionTitle = StyledLabel(VoipTheme.conference_invite_desc_title_font, VoipTexts.conference_description_title)
|
||||
let descriptionValue = StyledLabel(VoipTheme.conference_invite_desc_font)
|
||||
let joinShare = UIStackView()
|
||||
let join = FormButton(title:VoipTexts.conference_invite_join.uppercased(), backgroundStateColors: VoipTheme.button_green_background)
|
||||
let share = UIImageView(image:UIImage(named:"voip_export")?.tinted(with: VoipTheme.primaryTextColor.get()))
|
||||
|
||||
var icsFile : String? = nil
|
||||
|
||||
var conferenceData: ScheduledConferenceData? = nil {
|
||||
didSet {
|
||||
if let data = conferenceData {
|
||||
subject.text = data.subject.value
|
||||
participants.text = VoipTexts.conference_invite_participants_count.replacingOccurrences(of: "%d", with: String(data.conferenceInfo.participants.count+1))
|
||||
participants.addIndicatorIcon(iconName: "conference_schedule_participants_default",padding : 0.0, y: -indicator_y, trailing: false)
|
||||
date.text = " "+TimestampUtils.dateToString(date: data.rawDate)
|
||||
date.addIndicatorIcon(iconName: "conference_schedule_calendar_default", padding: 0.0, y:-indicator_y, trailing:false)
|
||||
timeDuration.text = " \(data.time.value) ( \(data.duration.value) )"
|
||||
timeDuration.addIndicatorIcon(iconName: "conference_schedule_time_default",padding : 0.0, y: -indicator_y, trailing: false)
|
||||
descriptionTitle.isHidden = data.description.value == nil || data.description.value!.count == 0
|
||||
descriptionValue.isHidden = descriptionTitle.isHidden
|
||||
descriptionValue.text = data.description.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
super.init(frame:.zero)
|
||||
|
||||
layer.cornerRadius = corner_radius
|
||||
clipsToBounds = true
|
||||
backgroundColor = VoipTheme.voip_light_gray
|
||||
|
||||
let rows = UIStackView()
|
||||
rows.axis = .vertical
|
||||
rows.spacing = rows_spacing
|
||||
|
||||
addSubview(rows)
|
||||
|
||||
rows.addArrangedSubview(inviteTitle)
|
||||
rows.addArrangedSubview(subject)
|
||||
rows.addArrangedSubview(participants)
|
||||
rows.addArrangedSubview(date)
|
||||
rows.addArrangedSubview(timeDuration)
|
||||
rows.addArrangedSubview(descriptionTitle)
|
||||
rows.addArrangedSubview(descriptionValue)
|
||||
|
||||
|
||||
addSubview(joinShare)
|
||||
joinShare.axis = .horizontal
|
||||
joinShare.spacing = rows_spacing
|
||||
joinShare.addArrangedSubview(share)
|
||||
share.square(share_size).done()
|
||||
joinShare.addArrangedSubview(join)
|
||||
rows.matchParentSideBorders(insetedByDx: inner_padding).alignParentTop(withMargin: inner_padding).done()
|
||||
joinShare.alignParentBottom(withMargin: inner_padding).width(join_share_width).alignParentRight(withMargin: inner_padding).done()
|
||||
|
||||
join.onClick {
|
||||
let view : ConferenceWaitingRoomFragment = self.VIEW(ConferenceWaitingRoomFragment.compositeViewDescription())
|
||||
PhoneMainView.instance().changeCurrentView(view.compositeViewDescription())
|
||||
view.setDetails(subject: (self.conferenceData?.subject.value)!, url: (self.conferenceData?.address.value)!)
|
||||
}
|
||||
|
||||
share.onClick {
|
||||
let ics = URL(string: "file://"+self.icsFile!)
|
||||
UIApplication.shared.open(ics!)
|
||||
}
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
@objc func setFromChatMessage(cmessage: OpaquePointer) {
|
||||
let message = ChatMessage.getSwiftObject(cObject: cmessage)
|
||||
message.contents.forEach { content in
|
||||
if (content.isIcalendar) {
|
||||
if let conferenceInfo = try? Factory.Instance.createConferenceInfoFromIcalendarContent(content: content) {
|
||||
self.conferenceData = ScheduledConferenceData(conferenceInfo: conferenceInfo)
|
||||
self.icsFile = content.filePath
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@objc static func isConferenceInvitationMessage(cmessage: OpaquePointer) -> Bool {
|
||||
var isConferenceInvitationMessage = false
|
||||
let message = ChatMessage.getSwiftObject(cObject: cmessage)
|
||||
message.contents.forEach { content in
|
||||
if (content.isIcalendar) {
|
||||
isConferenceInvitationMessage = true
|
||||
}
|
||||
}
|
||||
return isConferenceInvitationMessage
|
||||
}
|
||||
|
||||
@objc func setLayoutConstraints(view:UIView) {
|
||||
matchDimensionsWith(view: view, insetedByDx: inner_padding).done()
|
||||
}
|
||||
|
||||
}
|
||||
205
Classes/Swift/Conference/Views/ScheduledConferencesCell.swift
Normal file
205
Classes/Swift/Conference/Views/ScheduledConferencesCell.swift
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
import UIKit
|
||||
import Foundation
|
||||
import linphonesw
|
||||
|
||||
class ScheduledConferencesCell: UITableViewCell {
|
||||
|
||||
let corner_radius = 7.0
|
||||
let border_width = 2.0
|
||||
static let button_size = 40
|
||||
|
||||
let clockIcon = UIImageView(image: UIImage(named: "conference_schedule_time_default"))
|
||||
let timeDuration = StyledLabel(VoipTheme.conference_invite_desc_font)
|
||||
let organiser = StyledLabel(VoipTheme.conference_invite_desc_font)
|
||||
let subject = StyledLabel(VoipTheme.conference_invite_subject_font)
|
||||
let participantsIcon = UIImageView(image: UIImage(named: "conference_schedule_participants_default"))
|
||||
let participants = StyledLabel(VoipTheme.conference_invite_desc_font)
|
||||
let infoConf = UIButton()
|
||||
|
||||
let descriptionTitle = StyledLabel(VoipTheme.conference_invite_desc_font, VoipTexts.conference_description_title)
|
||||
let descriptionValue = StyledLabel(VoipTheme.conference_invite_desc_font)
|
||||
let urlTitle = StyledLabel(VoipTheme.conference_invite_desc_font, VoipTexts.conference_schedule_address_title)
|
||||
let urlValue = StyledLabel(VoipTheme.conference_scheduling_font)
|
||||
let copyLink = CallControlButton(width:button_size,height:button_size,buttonTheme: VoipTheme.scheduled_conference_action("voip_copy"))
|
||||
let joinConf = FormButton(title:VoipTexts.conference_invite_join.uppercased(), backgroundStateColors: VoipTheme.button_green_background)
|
||||
let deleteConf = CallControlButton(width:button_size,height:button_size,buttonTheme: VoipTheme.scheduled_conference_action("voip_delete"))
|
||||
let editConf = CallControlButton(width:button_size,height:button_size,buttonTheme: VoipTheme.scheduled_conference_action("voip_edit"))
|
||||
var owningTableView : UITableView? = nil
|
||||
let joinEditDelete = UIStackView()
|
||||
let expandedRows = UIStackView()
|
||||
|
||||
var conferenceData: ScheduledConferenceData? = nil {
|
||||
didSet {
|
||||
if let data = conferenceData {
|
||||
timeDuration.text = "\(data.time.value)"+(data.duration.value != nil ? " ( \(data.duration.value) )" : "")
|
||||
organiser.text = VoipTexts.conference_schedule_organizer+data.organizer.value!
|
||||
subject.text = data.subject.value!
|
||||
descriptionValue.text = data.description.value!
|
||||
urlValue.text = data.address.value!
|
||||
data.expanded.readCurrentAndObserve { expanded in
|
||||
self.contentView.layer.borderWidth = expanded == true ? 2.0 : 0.0
|
||||
self.descriptionTitle.isHidden = expanded != true || self.descriptionValue.text?.count == 0
|
||||
self.descriptionValue.isHidden = expanded != true || self.descriptionValue.text?.count == 0
|
||||
self.infoConf.isSelected = expanded == true
|
||||
self.participants.text = expanded == true ? data.participantsExpanded.value : data.participantsShort.value
|
||||
self.participants.numberOfLines = expanded == true ? 6 : 2
|
||||
self.expandedRows.isHidden = expanded != true
|
||||
self.joinEditDelete.isHidden = expanded != true
|
||||
if let myAddress = Core.get().defaultAccount?.params?.identityAddress {
|
||||
self.editConf.isHidden = expanded != true || data.conferenceInfo.organizer?.weakEqual(address2: myAddress) != true
|
||||
} else {
|
||||
self.editConf.isHidden = true
|
||||
}
|
||||
self.participants.removeConstraints().alignUnder(view: self.subject,withMargin: 15).toRightOf(self.participantsIcon,withLeftMargin:10).toRightOf(self.participantsIcon,withLeftMargin:10).toLeftOf(self.infoConf,withRightMargin: 15).done()
|
||||
self.joinEditDelete.removeConstraints().alignUnder(view: self.expandedRows,withMargin: 15).alignParentRight(withMargin: 10).done()
|
||||
if (expanded == true) {
|
||||
self.joinEditDelete.alignParentBottom(withMargin: 10).done()
|
||||
} else {
|
||||
self.participants.alignParentBottom(withMargin: 10).done()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
|
||||
contentView.layer.cornerRadius = corner_radius
|
||||
contentView.clipsToBounds = true
|
||||
contentView.backgroundColor = VoipTheme.header_background_color
|
||||
contentView.layer.borderColor = VoipTheme.primary_color.cgColor
|
||||
|
||||
|
||||
contentView.addSubview(clockIcon)
|
||||
clockIcon.alignParentTop(withMargin: 15).square(15).alignParentLeft(withMargin: 10).done()
|
||||
|
||||
contentView.addSubview(timeDuration)
|
||||
timeDuration.alignParentTop(withMargin: 15).toRightOf(clockIcon,withLeftMargin:10).alignHorizontalCenterWith(clockIcon).done()
|
||||
|
||||
contentView.addSubview(organiser)
|
||||
organiser.alignParentTop(withMargin: 15).toRightOf(timeDuration, withLeftMargin:10).alignParentRight(withMargin:10).alignHorizontalCenterWith(clockIcon).done()
|
||||
|
||||
contentView.addSubview(subject)
|
||||
subject.alignUnder(view: timeDuration,withMargin: 15).alignParentLeft(withMargin: 10).done()
|
||||
|
||||
contentView.addSubview(participantsIcon)
|
||||
participantsIcon.alignUnder(view: subject,withMargin: 15).square(15).alignParentLeft(withMargin: 10).done()
|
||||
|
||||
//infoConf.onClick {
|
||||
contentView.onClick {
|
||||
self.conferenceData?.toggleExpand()
|
||||
self.owningTableView?.reloadData()
|
||||
}
|
||||
contentView.addSubview(infoConf)
|
||||
infoConf.imageView?.contentMode = .scaleAspectFit
|
||||
infoConf.alignUnder(view: subject,withMargin: 15).square(30).alignParentRight(withMargin: 10).alignHorizontalCenterWith(participantsIcon).done()
|
||||
infoConf.applyTintedIcons(tintedIcons: VoipTheme.conference_info_button)
|
||||
|
||||
|
||||
contentView.addSubview(participants)
|
||||
participants.alignUnder(view: subject,withMargin: 15).toRightOf(participantsIcon,withLeftMargin:10).toRightOf(participantsIcon,withLeftMargin:10).toLeftOf(infoConf,withRightMargin: 15).done()
|
||||
|
||||
expandedRows.axis = .vertical
|
||||
expandedRows.spacing = 10
|
||||
contentView.addSubview(expandedRows)
|
||||
expandedRows.alignUnder(view: participants,withMargin: 15).matchParentSideBorders(insetedByDx:10).done()
|
||||
|
||||
expandedRows.addArrangedSubview(descriptionTitle)
|
||||
expandedRows.addArrangedSubview(descriptionValue)
|
||||
|
||||
expandedRows.addArrangedSubview(urlTitle)
|
||||
let urlAndCopy = UIStackView()
|
||||
urlAndCopy.addArrangedSubview(urlValue)
|
||||
urlValue.backgroundColor = .white
|
||||
self.urlValue.isEnabled = false
|
||||
urlValue.alignParentLeft().done()
|
||||
urlAndCopy.addArrangedSubview(copyLink)
|
||||
copyLink.toRightOf(urlValue,withLeftMargin: 10).done()
|
||||
expandedRows.addArrangedSubview(urlAndCopy)
|
||||
copyLink.onClick {
|
||||
UIPasteboard.general.string = self.conferenceData?.address.value!
|
||||
VoipDialog.toast(message: VoipTexts.conference_schedule_address_copied_to_clipboard)
|
||||
}
|
||||
|
||||
joinEditDelete.axis = .horizontal
|
||||
joinEditDelete.spacing = 10
|
||||
joinEditDelete.distribution = .equalSpacing
|
||||
|
||||
contentView.addSubview(joinEditDelete)
|
||||
joinEditDelete.alignUnder(view: expandedRows,withMargin: 15).alignParentRight(withMargin: 10).done()
|
||||
|
||||
|
||||
joinEditDelete.addArrangedSubview(joinConf)
|
||||
joinConf.width(150).done()
|
||||
joinConf.onClick {
|
||||
let view : ConferenceWaitingRoomFragment = self.VIEW(ConferenceWaitingRoomFragment.compositeViewDescription())
|
||||
PhoneMainView.instance().changeCurrentView(view.compositeViewDescription())
|
||||
view.setDetails(subject: (self.conferenceData?.subject.value)!, url: (self.conferenceData?.address.value)!)
|
||||
}
|
||||
|
||||
joinEditDelete.addArrangedSubview(editConf)
|
||||
editConf.onClick {
|
||||
guard let confData = self.conferenceData else {
|
||||
Log.e("Invalid conference date, unable to edit")
|
||||
VoipDialog.toast(message: VoipTexts.conference_edit_error)
|
||||
return
|
||||
}
|
||||
let infoDate = Date(timeIntervalSince1970: Double(confData.conferenceInfo.dateTime))
|
||||
ConferenceSchedulingViewModel.shared.reset()
|
||||
ConferenceSchedulingViewModel.shared.scheduledDate.value = infoDate
|
||||
ConferenceSchedulingViewModel.shared.scheduledTime.value = Date(timeIntervalSince1970: infoDate.timeIntervalSince1970 - Calendar.current.startOfDay(for: infoDate).timeIntervalSince1970)
|
||||
ConferenceSchedulingViewModel.shared.description.value = confData.description.value
|
||||
ConferenceSchedulingViewModel.shared.subject.value = confData.subject.value
|
||||
ConferenceSchedulingViewModel.shared.scheduledDuration.value = ConferenceSchedulingViewModel.durationList.firstIndex(where: {$0.value == confData.conferenceInfo.duration})
|
||||
ConferenceSchedulingViewModel.shared.scheduleForLater.value = true
|
||||
ConferenceSchedulingViewModel.shared.selectedAddresses.value = []
|
||||
confData.conferenceInfo.participants.forEach {
|
||||
ConferenceSchedulingViewModel.shared.selectedAddresses.value?.append($0)
|
||||
}
|
||||
ConferenceSchedulingViewModel.shared.existingConfInfo = confData.conferenceInfo
|
||||
// TOODO TimeZone (as Android 14.6.2022) ConferenceSchedulingViewModel.shared.scheduledTimeZone.value = self.conferenceData?.timezone
|
||||
let view : ConferenceSchedulingView = self.VIEW(ConferenceSchedulingView.compositeViewDescription())
|
||||
PhoneMainView.instance().changeCurrentView(view.compositeViewDescription())
|
||||
}
|
||||
|
||||
joinEditDelete.addArrangedSubview(deleteConf)
|
||||
deleteConf.onClick {
|
||||
let delete = ButtonAttributes(text:VoipTexts.conference_info_confirm_removal_delete, action: {
|
||||
Core.get().deleteConferenceInformation(conferenceInfo: self.conferenceData!.conferenceInfo)
|
||||
ScheduledConferencesViewModel.shared.computeConferenceInfoList()
|
||||
self.owningTableView?.reloadData()
|
||||
VoipDialog.toast(message: VoipTexts.conference_info_removed)
|
||||
}, isDestructive:false)
|
||||
let cancel = ButtonAttributes(text:VoipTexts.cancel, action: {}, isDestructive:true)
|
||||
VoipDialog(message:VoipTexts.conference_info_confirm_removal, givenButtons: [cancel,delete]).show()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
}
|
||||
124
Classes/Swift/Conference/Views/ScheduledConferencesView.swift
Normal file
124
Classes/Swift/Conference/Views/ScheduledConferencesView.swift
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
import UIKit
|
||||
import Foundation
|
||||
import linphonesw
|
||||
|
||||
@objc class ScheduledConferencesView: BackNextNavigationView, UICompositeViewDelegate, UITableViewDataSource, UITableViewDelegate {
|
||||
|
||||
let conferenceListView = UITableView()
|
||||
let noConference = StyledLabel(VoipTheme.empty_list_font,VoipTexts.conference_no_schedule)
|
||||
|
||||
static let compositeDescription = UICompositeViewDescription(ScheduledConferencesView.self, statusBar: StatusBarView.self, tabBar: nil, sideMenu: SideMenuView.self, fullscreen: false, isLeftFragment: false,fragmentWith: nil)
|
||||
static func compositeViewDescription() -> UICompositeViewDescription! { return compositeDescription }
|
||||
func compositeViewDescription() -> UICompositeViewDescription! { return type(of: self).compositeDescription }
|
||||
|
||||
override func viewDidLoad() {
|
||||
|
||||
super.viewDidLoad(
|
||||
backAction: {
|
||||
PhoneMainView.instance().popView(self.compositeViewDescription())
|
||||
},nextAction: {
|
||||
ConferenceSchedulingViewModel.shared.reset()
|
||||
PhoneMainView.instance().changeCurrentView(ConferenceSchedulingView.compositeDescription)
|
||||
},
|
||||
nextActionEnableCondition: MutableLiveData(),
|
||||
title:VoipTexts.conference_scheduled)
|
||||
|
||||
super.nextButton.applyTintedIcons(tintedIcons: VoipTheme.conference_create_button)
|
||||
|
||||
self.view.addSubview(conferenceListView)
|
||||
conferenceListView.isScrollEnabled = true
|
||||
conferenceListView.dataSource = self
|
||||
conferenceListView.delegate = self
|
||||
conferenceListView.register(ScheduledConferencesCell.self, forCellReuseIdentifier: "ScheduledConferencesCell")
|
||||
conferenceListView.allowsSelection = false
|
||||
conferenceListView.rowHeight = UITableView.automaticDimension
|
||||
if #available(iOS 15.0, *) {
|
||||
conferenceListView.allowsFocus = false
|
||||
}
|
||||
conferenceListView.separatorStyle = .singleLine
|
||||
conferenceListView.separatorColor = .white
|
||||
|
||||
view.addSubview(noConference)
|
||||
noConference.center().done()
|
||||
|
||||
}
|
||||
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
ScheduledConferencesViewModel.shared.computeConferenceInfoList()
|
||||
super.viewWillAppear(animated)
|
||||
self.conferenceListView.reloadData()
|
||||
self.conferenceListView.removeConstraints().done()
|
||||
self.conferenceListView.matchParentSideBorders(insetedByDx: 10).alignUnder(view: super.topBar,withMargin: self.form_margin).alignParentBottom().done()
|
||||
noConference.isHidden = !ScheduledConferencesViewModel.shared.daySplitted.isEmpty
|
||||
super.nextButton.isEnabled = Core.get().defaultAccount != nil
|
||||
}
|
||||
|
||||
// TableView datasource delegate
|
||||
|
||||
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
let daysArray = Array(ScheduledConferencesViewModel.shared.daySplitted.keys.sorted().reversed())
|
||||
let day = daysArray[section]
|
||||
return TimestampUtils.dateLongToString(date: day)
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
|
||||
guard let header = view as? UITableViewHeaderFooterView else { return }
|
||||
header.textLabel?.applyStyle(VoipTheme.conference_invite_title_font)
|
||||
header.textLabel?.matchParentSideBorders().done()
|
||||
}
|
||||
|
||||
func numberOfSections(in tableView: UITableView) -> Int {
|
||||
return ScheduledConferencesViewModel.shared.daySplitted.keys.count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
let daysArray = Array(ScheduledConferencesViewModel.shared.daySplitted.keys.sorted().reversed())
|
||||
let day = daysArray[section]
|
||||
return ScheduledConferencesViewModel.shared.daySplitted[day]!.count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
|
||||
let daysArray = Array(ScheduledConferencesViewModel.shared.daySplitted.keys.sorted().reversed())
|
||||
let day = daysArray[indexPath.section]
|
||||
guard let data = ScheduledConferencesViewModel.shared.daySplitted[day]?[indexPath.row] else {
|
||||
return UITableView.automaticDimension
|
||||
}
|
||||
return data.expanded.value! ? UITableView.automaticDimension : 100
|
||||
}
|
||||
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell:ScheduledConferencesCell = tableView.dequeueReusableCell(withIdentifier: "ScheduledConferencesCell") as! ScheduledConferencesCell
|
||||
let daysArray = Array(ScheduledConferencesViewModel.shared.daySplitted.keys.sorted().reversed())
|
||||
let day = daysArray[indexPath.section]
|
||||
guard let data = ScheduledConferencesViewModel.shared.daySplitted[day]?[indexPath.row] else {
|
||||
return cell
|
||||
}
|
||||
cell.conferenceData = data
|
||||
cell.owningTableView = tableView
|
||||
return cell
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -17,22 +17,27 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#import "UIMutedMicroButton.h"
|
||||
|
||||
#import "LinphoneManager.h"
|
||||
|
||||
@implementation UIMutedMicroButton
|
||||
|
||||
- (void)onOn {
|
||||
linphone_core_enable_mic(LC, false);
|
||||
extension Optional {
|
||||
var orNil: Any {
|
||||
switch self {
|
||||
case .none:
|
||||
return "<nil>|⭕️"
|
||||
case let .some(value):
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onOff {
|
||||
linphone_core_enable_mic(LC, true);
|
||||
extension Optional: CustomStringConvertible {
|
||||
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .some(let wrappedValue):
|
||||
return "\(wrappedValue)"
|
||||
default:
|
||||
return "<nil>|⭕️"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (bool)onUpdate {
|
||||
return (linphone_core_get_current_call(LC) || linphone_core_is_in_conference(LC)) && !linphone_core_mic_enabled(LC);
|
||||
}
|
||||
|
||||
@end
|
||||
39
Classes/Swift/Extensions/IOS/UIApplication+Extension.swift
Normal file
39
Classes/Swift/Extensions/IOS/UIApplication+Extension.swift
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linhome
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
|
||||
extension UIApplication {
|
||||
|
||||
class func getTopMostViewController() -> UIViewController? {
|
||||
let keyWindow = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
|
||||
if var topController = keyWindow?.rootViewController {
|
||||
while let presentedViewController = topController.presentedViewController {
|
||||
topController = presentedViewController
|
||||
}
|
||||
return topController
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -17,20 +17,24 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#import "UIBluetoothButton.h"
|
||||
#import "../Utils/AudioHelper.h"
|
||||
#import "Utils.h"
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
import Foundation
|
||||
import SnapKit
|
||||
import UIKit
|
||||
|
||||
#include "linphone/linphonecore.h"
|
||||
|
||||
@implementation UIBluetoothButton
|
||||
#define check_auresult(au, method) \
|
||||
if (au != 0) \
|
||||
LOGE(@"UIBluetoothButton error for %s: ret=%ld", method, au)
|
||||
|
||||
- (bool)onUpdate {
|
||||
return false;
|
||||
extension UIButton {
|
||||
func addSidePadding(p:CGFloat = 10) {
|
||||
if let w = titleLabel?.textWidth {
|
||||
width(w+2*p).done()
|
||||
}
|
||||
}
|
||||
|
||||
func applyTintedIcons(tintedIcons: [UInt: TintableIcon]) {
|
||||
tintedIcons.keys.forEach { (stateRawValue) in
|
||||
let tintedIcon = tintedIcons[stateRawValue]!
|
||||
UIImage(named:tintedIcon.name).map {
|
||||
setImage($0.tinted(with: tintedIcon.tintColor?.get()),for: UIButton.State(rawValue: stateRawValue))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
40
Classes/Swift/Extensions/IOS/UIColorExtensions.swift
Normal file
40
Classes/Swift/Extensions/IOS/UIColorExtensions.swift
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
extension UIColor {
|
||||
public convenience init(hex: String) {
|
||||
let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
|
||||
var int = UInt64()
|
||||
Scanner(string: hex).scanHexInt64(&int)
|
||||
let a, r, g, b: UInt64
|
||||
switch hex.count {
|
||||
case 3: // RGB (12-bit)
|
||||
(a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
|
||||
case 6: // RGB (24-bit)
|
||||
(a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
|
||||
case 8: // ARGB (32-bit)
|
||||
(a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
|
||||
default:
|
||||
(a, r, g, b) = (255, 0, 0, 0)
|
||||
}
|
||||
self.init(red: CGFloat(r) / 255, green: CGFloat(g) / 255, blue: CGFloat(b) / 255, alpha: CGFloat(a) / 255)
|
||||
}
|
||||
}
|
||||
57
Classes/Swift/Extensions/IOS/UIDeviceExtensions.swift
Normal file
57
Classes/Swift/Extensions/IOS/UIDeviceExtensions.swift
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linhome
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AVFoundation
|
||||
|
||||
extension UIDevice {
|
||||
static func ipad() -> Bool {
|
||||
return UIDevice.current.userInterfaceIdiom == .pad
|
||||
}
|
||||
static func vibrate() {
|
||||
if (!ipad()) {
|
||||
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate)
|
||||
}
|
||||
}
|
||||
static func is5SorSEGen1() -> Bool {
|
||||
return UIScreen.main.nativeBounds.height == 1136
|
||||
}
|
||||
|
||||
static func hasNotch() -> Bool {
|
||||
if (UserDefaults.standard.bool(forKey: "hasNotch")) {
|
||||
return true
|
||||
}
|
||||
guard #available(iOS 11.0, *), let topPadding = UIApplication.shared.keyWindow?.safeAreaInsets.top, topPadding > 24 else {
|
||||
return false
|
||||
}
|
||||
UserDefaults.standard.setValue(true, forKey: "hasNotch")
|
||||
return true
|
||||
}
|
||||
|
||||
static func notchHeight() -> CGFloat {
|
||||
guard #available(iOS 11.0, *), let topPadding = UIApplication.shared.keyWindow?.safeAreaInsets.top else {
|
||||
return 0
|
||||
}
|
||||
return topPadding
|
||||
}
|
||||
|
||||
}
|
||||
50
Classes/Swift/Extensions/IOS/UIImageExtensions.swift
Normal file
50
Classes/Swift/Extensions/IOS/UIImageExtensions.swift
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
extension UIImage {
|
||||
func tinted(with color: UIColor?) -> UIImage? {
|
||||
if (color == nil) {
|
||||
return self
|
||||
}
|
||||
defer { UIGraphicsEndImageContext() }
|
||||
UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
|
||||
color!.set()
|
||||
self.withRenderingMode(.alwaysTemplate).draw(in: CGRect(origin: .zero, size: self.size))
|
||||
return UIGraphicsGetImageFromCurrentImageContext()
|
||||
}
|
||||
|
||||
func withInsets(insets: UIEdgeInsets) -> UIImage? {
|
||||
UIGraphicsBeginImageContextWithOptions(
|
||||
CGSize(width: self.size.width + insets.left + insets.right,
|
||||
height: self.size.height + insets.top + insets.bottom), false, self.scale)
|
||||
let _ = UIGraphicsGetCurrentContext()
|
||||
let origin = CGPoint(x: insets.left, y: insets.top)
|
||||
self.draw(at: origin)
|
||||
let imageWithInsets = UIGraphicsGetImageFromCurrentImageContext()
|
||||
UIGraphicsEndImageContext()
|
||||
return imageWithInsets
|
||||
}
|
||||
|
||||
func withPadding(padding: CGFloat) -> UIImage? {
|
||||
let insets = UIEdgeInsets(top: padding, left: padding, bottom: padding, right: padding)
|
||||
return withInsets(insets: insets)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -17,11 +17,11 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "UIToggleButton.h"
|
||||
|
||||
@interface UIBluetoothButton : UIToggleButton<UIToggleButtonDelegate> {
|
||||
import Foundation
|
||||
|
||||
extension UIImageView {
|
||||
func tint(_ color:UIColor) {
|
||||
self.image = self.image?.withRenderingMode(.alwaysTemplate)
|
||||
tintColor = color
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
32
Classes/Swift/Extensions/IOS/UILabelExtensions.swift
Normal file
32
Classes/Swift/Extensions/IOS/UILabelExtensions.swift
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import SnapKit
|
||||
import UIKit
|
||||
|
||||
extension UILabel {
|
||||
var textWidth: CGFloat? {
|
||||
guard let myText = self.text else { return nil }
|
||||
guard let myFont = self.font else { return nil }
|
||||
let rect = CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)
|
||||
let labelSize = myText.boundingRect(with: rect, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: myFont], context: nil)
|
||||
return ceil(labelSize.width)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -17,11 +17,13 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
import Foundation
|
||||
import SnapKit
|
||||
import UIKit
|
||||
|
||||
#import "UIToggleButton.h"
|
||||
|
||||
@interface UIMutedMicroButton : UIToggleButton <UIToggleButtonDelegate> {
|
||||
extension UIViewController {
|
||||
func VIEW<T>( _ desc: UICompositeViewDescription) -> T{
|
||||
return PhoneMainView.instance().mainViewController.getCachedController(desc.name) as! T
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
418
Classes/Swift/Extensions/IOS/UIVIewExtensions.swift
Normal file
418
Classes/Swift/Extensions/IOS/UIVIewExtensions.swift
Normal file
|
|
@ -0,0 +1,418 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import SnapKit
|
||||
import UIKit
|
||||
|
||||
extension UIView {
|
||||
|
||||
// Few constraints wrapper to abstract SnapKit functions
|
||||
|
||||
func removeConstraints() -> UIView {
|
||||
snp.removeConstraints()
|
||||
return self
|
||||
}
|
||||
|
||||
|
||||
func square(_ size:Int) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.width.equalTo(size)
|
||||
make.height.equalTo(size)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
|
||||
func makeHeightMatchWidth() -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.height.equalTo(snp.width)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func size(w:CGFloat,h:CGFloat) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.width.equalTo(w)
|
||||
make.height.equalTo(h)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func height(_ h:CGFloat) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.height.equalTo(h)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func height(_ h:Int) -> UIView {
|
||||
return height(CGFloat(h))
|
||||
}
|
||||
|
||||
func width(_ h:CGFloat) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.width.equalTo(h)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func width(_ h:Int) -> UIView {
|
||||
return width(CGFloat(h))
|
||||
}
|
||||
|
||||
func maxHeight(_ h:CGFloat) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.height.lessThanOrEqualTo(h)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func minWidth(_ h:CGFloat) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.width.greaterThanOrEqualTo(h)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
|
||||
func matchParentSideBorders(insetedByDx:CGFloat = 0) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.left.equalToSuperview().offset(insetedByDx)
|
||||
make.right.equalToSuperview().offset(-insetedByDx)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func matchBordersOf(view:UIView) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.left.right.equalTo(view)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func matchParentDimmensions() -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.left.right.top.bottom.equalToSuperview()
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func matchParentDimmensions(insetedByDx:CGFloat) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.left.top.equalToSuperview().offset(insetedByDx)
|
||||
make.right.bottom.equalToSuperview().offset(-insetedByDx)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func matchDimensionsWith(view:UIView, insetedByDx:CGFloat = 0) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.left.top.equalTo(view).offset(insetedByDx)
|
||||
make.right.bottom.equalTo(view).offset(-insetedByDx)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func matchParentEdges() -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.edges.equalToSuperview()
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func matchDimentionsOf(view:UIView) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.left.right.top.bottom.equalTo(view)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func matchParentHeight() -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.top.bottom.equalToSuperview()
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func addRightMargin(margin:CGFloat) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.rightMargin.equalTo(margin)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func matchParentHeightDividedBy(_ divider : CGFloat) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.height.equalToSuperview().dividedBy(divider)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func matchParentWidthDividedBy(_ divider : CGFloat) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.width.equalToSuperview().dividedBy(divider)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func center() -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.center.equalToSuperview()
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func alignParentTop(withMargin:CGFloat = 0.0) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.top.equalToSuperview().offset(withMargin)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func alignParentTop(withMargin:Int ) -> UIView {
|
||||
return alignParentTop(withMargin:CGFloat(withMargin))
|
||||
}
|
||||
|
||||
|
||||
func alignUnder(view:UIView, withMargin:CGFloat = 0.0) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.top.equalTo(view.snp.bottom).offset(withMargin)
|
||||
}
|
||||
return self
|
||||
}
|
||||
func alignUnder(view:UIView, withMargin:Int) -> UIView {
|
||||
return alignUnder(view: view,withMargin:CGFloat(withMargin))
|
||||
}
|
||||
|
||||
func matchRightOf(view:UIView, withMargin:CGFloat = 0) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.right.equalTo(view).offset(withMargin)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func updateAlignUnder(view:UIView, withMargin:CGFloat = 0.0) -> UIView {
|
||||
snp.updateConstraints { (make) in
|
||||
make.top.equalTo(view.snp.bottom).offset(withMargin)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func alignParentBottom(withMargin:CGFloat = 0.0) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.bottom.equalToSuperview().offset(-withMargin)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func alignParentBottom(withMargin:Int) -> UIView {
|
||||
return alignParentBottom(withMargin:CGFloat(withMargin))
|
||||
}
|
||||
|
||||
func alignAbove(view:UIView, withMargin:CGFloat = 0.0) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.bottom.equalTo(view.snp.top).offset(-withMargin)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func alignAbove(view:UIView, withMargin:Int) -> UIView {
|
||||
return alignAbove(view: view,withMargin:CGFloat(withMargin))
|
||||
}
|
||||
|
||||
func alignBottomWith(otherView:UIView) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.bottom.equalTo(otherView)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func marginLeft(_ m:CGFloat) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.left.equalToSuperview().offset(m)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func alignParentLeft(withMargin:CGFloat = 0.0) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.left.equalToSuperview().offset(withMargin)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func alignParentLeft(withMargin:Int) -> UIView {
|
||||
return alignParentLeft(withMargin:CGFloat(withMargin))
|
||||
}
|
||||
|
||||
func alignParentRight(withMargin:Int = 0) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.right.equalToSuperview().offset(-withMargin)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func alignParentRight(withMargin:CGFloat) -> UIView {
|
||||
return alignParentRight(withMargin:Int(withMargin))
|
||||
}
|
||||
|
||||
|
||||
func toRightOf(_ view:UIView, withLeftMargin:Int = 0) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.left.equalTo(view.snp.right).offset(withLeftMargin)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func toRightOf(_ view:UIView, withLeftMargin:CGFloat) -> UIView {
|
||||
return toRightOf(view,withLeftMargin: Int(withLeftMargin))
|
||||
}
|
||||
|
||||
|
||||
func alignHorizontalCenterWith(_ view:UIView) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.centerY.equalTo(view)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func alignVerticalCenterWith(_ view:UIView) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.centerX.equalTo(view)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
|
||||
func toLeftOf(_ view:UIView) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.right.equalTo(view.snp.left)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func toLeftOf(_ view:UIView, withRightMargin:CGFloat) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.right.equalTo(view.snp.left).offset(-withRightMargin)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func centerX(withDx:Int = 0) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.centerX.equalToSuperview().offset(withDx)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func centerY(withDy:Int = 0) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.centerY.equalToSuperview().offset(withDy)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func matchCenterXOf(view:UIView, withDx:Int = 0) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.centerX.equalTo(view).offset(withDx)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func matchCenterYOf(view:UIView, withDy:Int = 0) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.centerY.equalTo(view).offset(withDy)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func wrapContentY() -> UIView {
|
||||
subviews.first?.snp.makeConstraints({ make in
|
||||
make.top.equalToSuperview()
|
||||
})
|
||||
subviews.last?.snp.makeConstraints({ make in
|
||||
make.bottom.equalToSuperview()
|
||||
})
|
||||
return self
|
||||
}
|
||||
|
||||
func wrapContentX() -> UIView {
|
||||
subviews.first?.snp.makeConstraints({ make in
|
||||
make.left.equalToSuperview()
|
||||
})
|
||||
subviews.last?.snp.makeConstraints({ make in
|
||||
make.right.equalToSuperview()
|
||||
})
|
||||
return self
|
||||
}
|
||||
|
||||
func done() {
|
||||
// to avoid the unused variable warning
|
||||
}
|
||||
|
||||
// Onclick
|
||||
|
||||
class TapGestureRecognizer: UITapGestureRecognizer {
|
||||
var action : (()->Void)? = nil
|
||||
}
|
||||
|
||||
func onClick(action : @escaping ()->Void ){
|
||||
let tap = TapGestureRecognizer(target: self , action: #selector(self.handleTap(_:)))
|
||||
tap.action = action
|
||||
tap.numberOfTapsRequired = 1
|
||||
tap.cancelsTouchesInView = false
|
||||
|
||||
self.addGestureRecognizer(tap)
|
||||
self.isUserInteractionEnabled = true
|
||||
|
||||
}
|
||||
@objc func handleTap(_ sender: TapGestureRecognizer) {
|
||||
sender.action!()
|
||||
}
|
||||
|
||||
func VIEW<T>( _ desc: UICompositeViewDescription) -> T{
|
||||
return PhoneMainView.instance().mainViewController.getCachedController(desc.name) as! T
|
||||
}
|
||||
|
||||
// Theming
|
||||
|
||||
func setFormInputBackground(readOnly:Bool) {
|
||||
if (readOnly) {
|
||||
backgroundColor = VoipTheme.voipFormDisabledFieldBackgroundColor.get()
|
||||
} else {
|
||||
layer.borderWidth = 1
|
||||
layer.borderColor = VoipTheme.voipFormFieldBackgroundColor.get().cgColor
|
||||
}
|
||||
layer.cornerRadius = 3
|
||||
clipsToBounds = true
|
||||
}
|
||||
|
||||
@objc func toImage() -> UIImage? {
|
||||
UIGraphicsBeginImageContextWithOptions(bounds.size, false, 0)
|
||||
guard let context = UIGraphicsGetCurrentContext() else { return nil }
|
||||
context.saveGState()
|
||||
layer.render(in: context)
|
||||
context.restoreGState()
|
||||
guard let image = UIGraphicsGetImageFromCurrentImageContext() else { return nil }
|
||||
UIGraphicsEndImageContext()
|
||||
return image
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import linphonesw
|
||||
|
||||
extension Address {
|
||||
|
||||
func initials() -> String? {
|
||||
var initials = initials(displayName: addressBookEnhancedDisplayName())
|
||||
if (initials == nil || initials!.isEmpty) {
|
||||
initials = String(username.prefix(1))
|
||||
}
|
||||
return initials
|
||||
}
|
||||
|
||||
private func initials(displayName: String?) -> String? { // Basic ImproveMe
|
||||
return displayName?.components(separatedBy: " ")
|
||||
.reduce("") {
|
||||
($0.isEmpty ? "" : "\($0.first?.uppercased() ?? "")") +
|
||||
($1.isEmpty ? "" : "\($1.first?.uppercased() ?? "")")
|
||||
}
|
||||
}
|
||||
|
||||
func addressBookEnhancedDisplayName() -> String? {
|
||||
if let contact = FastAddressBook.getContactWith(getCobject) {
|
||||
return contact.displayName
|
||||
} else if (!displayName.isEmpty) {
|
||||
return displayName
|
||||
} else {
|
||||
return username
|
||||
}
|
||||
}
|
||||
|
||||
func contact() -> Contact? {
|
||||
return FastAddressBook.getContactWith(getCobject)
|
||||
}
|
||||
|
||||
}
|
||||
48
Classes/Swift/Extensions/LinphoneCore/CallExtensions.swift
Normal file
48
Classes/Swift/Extensions/LinphoneCore/CallExtensions.swift
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import linphonesw
|
||||
|
||||
extension Call {
|
||||
func answerVideoUpdateRequest(accept:Bool) {
|
||||
guard let params = try?core? .createCallParams(call: self) else {
|
||||
Log.i("[Call] \(self) unable to answerVideoUpdateRequest : could not create params ")
|
||||
return
|
||||
}
|
||||
if (accept) {
|
||||
params.videoEnabled = true
|
||||
core?.videoCaptureEnabled = true
|
||||
core?.videoDisplayEnabled = true
|
||||
} else {
|
||||
params.videoEnabled = false
|
||||
}
|
||||
try?acceptUpdate(params: params)
|
||||
}
|
||||
}
|
||||
|
||||
extension Call : CustomStringConvertible {
|
||||
public var description: String {
|
||||
if let callId = callLog?.callId {
|
||||
return "<Call-ID: \(callId)>"
|
||||
}
|
||||
return "<Raw pointer:\(Unmanaged.passUnretained(self).toOpaque())>"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import linphonesw
|
||||
|
||||
|
||||
|
||||
extension Conference : CustomStringConvertible {
|
||||
public var description: String {
|
||||
if let username = conferenceAddress?.username {
|
||||
return "<\(username)>"
|
||||
}
|
||||
return "<pointer:\(Unmanaged.passUnretained(self).toOpaque())>"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
43
Classes/Swift/Extensions/LinphoneCore/CoreExtensions.swift
Normal file
43
Classes/Swift/Extensions/LinphoneCore/CoreExtensions.swift
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import linphonesw
|
||||
|
||||
extension Core {
|
||||
static func get() -> Core {
|
||||
return CallManager.instance().lc!
|
||||
}
|
||||
|
||||
func showSwitchCameraButton() -> Bool {
|
||||
return videoDevicesList.count > 2 // Count StaticImage camera
|
||||
}
|
||||
|
||||
func toggleCamera() {
|
||||
Log.i("[Core] Current camera device is \(videoDevice)")
|
||||
var switched = false
|
||||
videoDevicesList.forEach {
|
||||
if (!switched && $0 != videoDevice && $0 != "StaticImage: Static picture") {
|
||||
Log.i("[Core] New camera device will be \($0)")
|
||||
try?setVideodevice(newValue: $0)
|
||||
switched = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue