Commit b82c2bf5 authored by Kateryna Kostiuk's avatar Kateryna Kostiuk Committed by Andreas Traczyk

call: refine call options

This patch adds the next changes:
- refactor call screen UI
- introduce audio service
- add switch speakerphone
- add headset support

Change-Id: Ie17df97eb64b6d0f6912451e69d67ce8647b4c38
parent e455c9bf
......@@ -114,6 +114,9 @@
0E49097A1FEAC9E1005CAA50 /* CallViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E4909791FEAC9E1005CAA50 /* CallViewController.swift */; };
0E49097C1FEACA4B005CAA50 /* CallViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E49097B1FEACA4B005CAA50 /* CallViewModel.swift */; };
0E6949791FA7E71C0029B60A /* BaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6949781FA7E71C0029B60A /* BaseViewController.swift */; };
0E7CF4DB20164B6700CD967D /* ButtonsContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7CF4DA20164B6700CD967D /* ButtonsContainerView.swift */; };
0E7CF4DD20165BFB00CD967D /* ButtonsContainerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7CF4DC20165BFB00CD967D /* ButtonsContainerViewModel.swift */; };
0E7CF4DF2017918300CD967D /* ButtonsContainerView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0E7CF4DE2017918300CD967D /* ButtonsContainerView.xib */; };
0E983E6E1FC77C3E0082103E /* ConversationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E983E6D1FC77C3E0082103E /* ConversationModel.swift */; };
0E9D84491FA7DA6A00C561EB /* ChatTabBarItemViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9D84481FA7DA6A00C561EB /* ChatTabBarItemViewModel.swift */; };
0E9D844B1FA7DBAA00C561EB /* ContactRequestTabBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9D844A1FA7DBAA00C561EB /* ContactRequestTabBarItem.swift */; };
......@@ -241,6 +244,8 @@
62AA15BF1FFC36840064A063 /* VideoAdapter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 62AA15BE1FFC36840064A063 /* VideoAdapter.mm */; };
62AA15C31FFC39C80064A063 /* VideoAdapterDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62AA15C21FFC39C80064A063 /* VideoAdapterDelegate.swift */; };
62AA15CA1FFD3D7E0064A063 /* VideoService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62AA15C91FFD3D7E0064A063 /* VideoService.swift */; };
62AF685E201A61FF003AA9E8 /* AudioService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62AF685D201A61FF003AA9E8 /* AudioService.swift */; };
62AF6862201A66CF003AA9E8 /* AudioAdapter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 62AF6861201A66CF003AA9E8 /* AudioAdapter.mm */; };
62DFAB2C1F9FF030002D6F9C /* Reachability.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 62DFAB2B1F9FF030002D6F9C /* Reachability.framework */; };
62DFAB2E1F9FF0D0002D6F9C /* NetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62DFAB2D1F9FF0D0002D6F9C /* NetworkService.swift */; };
62E55B6D1F758E6F00D3FEF4 /* String+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62E55B6C1F758E6F00D3FEF4 /* String+Helpers.swift */; };
......@@ -375,6 +380,9 @@
0E4909791FEAC9E1005CAA50 /* CallViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallViewController.swift; sourceTree = "<group>"; };
0E49097B1FEACA4B005CAA50 /* CallViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallViewModel.swift; sourceTree = "<group>"; };
0E6949781FA7E71C0029B60A /* BaseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseViewController.swift; sourceTree = "<group>"; };
0E7CF4DA20164B6700CD967D /* ButtonsContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonsContainerView.swift; sourceTree = "<group>"; };
0E7CF4DC20165BFB00CD967D /* ButtonsContainerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonsContainerViewModel.swift; sourceTree = "<group>"; };
0E7CF4DE2017918300CD967D /* ButtonsContainerView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ButtonsContainerView.xib; sourceTree = "<group>"; };
0E983E6D1FC77C3E0082103E /* ConversationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationModel.swift; sourceTree = "<group>"; };
0E9D84481FA7DA6A00C561EB /* ChatTabBarItemViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatTabBarItemViewModel.swift; sourceTree = "<group>"; };
0E9D844A1FA7DBAA00C561EB /* ContactRequestTabBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactRequestTabBarItem.swift; sourceTree = "<group>"; };
......@@ -561,6 +569,9 @@
62AD0C281FE03FF600BEA1F6 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
62AD0C2A1FE054DD00BEA1F6 /* zh-Hans-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans-CN"; path = "zh-Hans-CN.lproj/Localizable.strings"; sourceTree = "<group>"; };
62AD0C2B1FE0557D00BEA1F6 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; };
62AF685D201A61FF003AA9E8 /* AudioService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioService.swift; sourceTree = "<group>"; };
62AF6861201A66CF003AA9E8 /* AudioAdapter.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AudioAdapter.mm; sourceTree = "<group>"; };
62AF6863201A66F0003AA9E8 /* AudioAdapter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AudioAdapter.h; sourceTree = "<group>"; };
62DFAB2B1F9FF030002D6F9C /* Reachability.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Reachability.framework; path = Carthage/Build/iOS/Reachability.framework; sourceTree = "<group>"; };
62DFAB2D1F9FF0D0002D6F9C /* NetworkService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkService.swift; sourceTree = "<group>"; };
62E55B6C1F758E6F00D3FEF4 /* String+Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Helpers.swift"; sourceTree = "<group>"; };
......@@ -730,6 +741,7 @@
0E49096D1FEAC0DE005CAA50 /* CallsService.swift */,
62AA15C21FFC39C80064A063 /* VideoAdapterDelegate.swift */,
62AA15C91FFD3D7E0064A063 /* VideoService.swift */,
62AF685D201A61FF003AA9E8 /* AudioService.swift */,
);
path = Services;
sourceTree = "<group>";
......@@ -767,6 +779,8 @@
0E4909691FEAB156005CAA50 /* CallsAdapter.mm */,
62AA15BD1FFC366D0064A063 /* VideoAdapter.h */,
62AA15BE1FFC36840064A063 /* VideoAdapter.mm */,
62AF6861201A66CF003AA9E8 /* AudioAdapter.mm */,
62AF6863201A66F0003AA9E8 /* AudioAdapter.h */,
);
path = Bridging;
sourceTree = "<group>";
......@@ -967,6 +981,9 @@
0E4909741FEAC943005CAA50 /* CallViewController.storyboard */,
0E4909791FEAC9E1005CAA50 /* CallViewController.swift */,
0E49097B1FEACA4B005CAA50 /* CallViewModel.swift */,
0E7CF4DA20164B6700CD967D /* ButtonsContainerView.swift */,
0E7CF4DC20165BFB00CD967D /* ButtonsContainerViewModel.swift */,
0E7CF4DE2017918300CD967D /* ButtonsContainerView.xib */,
);
path = Calls;
sourceTree = "<group>";
......@@ -1504,6 +1521,7 @@
1A5DC03E1F35678D0075E8EF /* ContactRequestsViewController.storyboard in Resources */,
0EB1A5CF1F8EBE03009923E2 /* DeviceCell.xib in Resources */,
1A2D18B31F2915C500B2C785 /* ConversationViewController.storyboard in Resources */,
0E7CF4DF2017918300CD967D /* ButtonsContainerView.xib in Resources */,
1A2D18A01F27A6D600B2C785 /* LinkDeviceViewController.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
......@@ -1638,6 +1656,7 @@
56BBC99F1ED714CB00CDAF8B /* MessagesAdapter.mm in Sources */,
0E49096A1FEAB156005CAA50 /* CallsAdapter.mm in Sources */,
1A2D18A61F27F7A400B2C785 /* UIViewController+Rx.swift in Sources */,
0E7CF4DB20164B6700CD967D /* ButtonsContainerView.swift in Sources */,
0E49097A1FEAC9E1005CAA50 /* CallViewController.swift in Sources */,
1A5DC0241F3564360075E8EF /* ContactRequestModel.swift in Sources */,
0E4909701FEAC1C6005CAA50 /* CallModel.swift in Sources */,
......@@ -1674,6 +1693,7 @@
1A2D189A1F2642C000B2C785 /* NotificationCenter+Ring.swift in Sources */,
1A2D18FC1F292DAD00B2C785 /* ConversationCell.swift in Sources */,
0E48F9D31FDF150700D6CC08 /* ContactRequestManager.swift in Sources */,
0E7CF4DD20165BFB00CD967D /* ButtonsContainerViewModel.swift in Sources */,
1A5DC0371F35675E0075E8EF /* ContactRequestCell.swift in Sources */,
1A20417C1F1E56FF00C08435 /* WelcomeViewModel.swift in Sources */,
1A5DC03D1F35678D0075E8EF /* ContactRequestItem.swift in Sources */,
......@@ -1682,6 +1702,7 @@
1A0C4EE31F1D673600550433 /* InjectionBag.swift in Sources */,
564C44641E943E1E000F92B1 /* NameRegistrationAdapterDelegate.swift in Sources */,
1A2D18AA1F29131900B2C785 /* ConversationsCoordinator.swift in Sources */,
62AF685E201A61FF003AA9E8 /* AudioService.swift in Sources */,
043999F71D1C2D9D00E99CD9 /* AppDelegate.swift in Sources */,
1A2041861F1EA19600C08435 /* CreateAccountViewController.swift in Sources */,
0EDCC8601F98150500B121D7 /* UIView+Rx.swift in Sources */,
......@@ -1711,6 +1732,7 @@
0273C3081E0C68BF00CF00BA /* DesignableButton.swift in Sources */,
1A5DC0321F3566140075E8EF /* ConversationSection.swift in Sources */,
1A2D18C41F29180700B2C785 /* ConfigKeyModel.swift in Sources */,
62AF6862201A66CF003AA9E8 /* AudioAdapter.mm in Sources */,
1A20417A1F1E547F00C08435 /* Stateable.swift in Sources */,
1A2D18F51F292D7200B2C785 /* MessageCellReceived.swift in Sources */,
56BBC9BC1ED7161200CDAF8B /* Date+Helpers.swift in Sources */,
......
......@@ -38,6 +38,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
private let presenceService = PresenceService(withPresenceAdapter: PresenceAdapter())
private let callService = CallsService(withCallsAdapter: CallsAdapter())
private let videoService = VideoService(withVideoAdapter: VideoAdapter())
private let audioService = AudioService(withAudioAdapter: AudioAdapter())
private let networkService = NetworkService()
private var conversationManager: ConversationsManager?
private var contactRequestManager: ContactRequestManager?
......@@ -51,7 +52,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
withPresenceService: self.presenceService,
withNetworkService: self.networkService,
withCallService: self.callService,
withVideoService: self.videoService)
withVideoService: self.videoService,
withAudioService: self.audioService)
}()
private lazy var appCoordinator: AppCoordinator = {
return AppCoordinator(with: self.injectionBag)
......@@ -86,6 +88,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
// start monitoring for network changes
self.networkService.monitorNetworkType()
// set device to headset if present
self.audioService.overrideAudioRoute(.override)
// themetize the app
Chameleon.setGlobalThemeUsingPrimaryColor(UIColor.ringMain, withSecondaryColor: UIColor.ringSecondary, andContentStyle: .light)
Chameleon.setRingThemeUsingPrimaryColor(UIColor.ringMain, withSecondaryColor: UIColor.ringSecondary, andContentStyle: .light)
......
/*
* Copyright (C) 2018 Savoir-faire Linux Inc.
*
* Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#import <Foundation/Foundation.h>
@interface AudioAdapter : NSObject
- (void)setAudioOutputDevice:(NSInteger)index;
- (void)setAudioInputDevice:(NSInteger)index;
- (void)setAudioRingtoneDevice:(NSInteger)index;
@end
/*
* Copyright (C) 2018 Savoir-faire Linux Inc.
*
* Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#import "AudioAdapter.h"
#import "dring/configurationmanager_interface.h"
@implementation AudioAdapter
- (void)setAudioOutputDevice:(NSInteger)index {
DRing::setAudioOutputDevice((int32_t)index);
}
- (void)setAudioInputDevice:(NSInteger)index {
DRing::setAudioInputDevice((int32_t)index);
}
- (void)setAudioRingtoneDevice:(NSInteger)index {
DRing::setAudioRingtoneDevice((int32_t)index);
}
@end
......@@ -33,7 +33,7 @@
- (BOOL)holdCallWithId:(NSString*)callId;
- (BOOL)unholdCallWithId:(NSString*)callId;
- (NSString*)placeCallWithAccountId:(NSString*)accountId toRingId:(NSString*)ringId;
- (NSString*)placeCallWithAccountId:(NSString*)accountId toRingId:(NSString*)ringId details:(NSDictionary*)details;
- (NSDictionary<NSString*,NSString*>*)callDetailsWithCallId:(NSString*)callId;
- (NSArray<NSString*>*)calls;
- (void) sendTextMessageWithCallID:(NSString*)callId message:(NSDictionary*)message accountId:(NSString*)accountId sMixed:(bool)isMixed;
......
......@@ -155,8 +155,9 @@ static id <CallsAdapterDelegate> _delegate;
return unhold(std::string([callId UTF8String]));
}
- (NSString*)placeCallWithAccountId:(NSString*)accountId toRingId:(NSString*)ringId {
std::string callId = placeCall(std::string([accountId UTF8String]), std::string([ringId UTF8String]));
- (NSString*)placeCallWithAccountId:(NSString*)accountId toRingId:(NSString*)ringId details:(NSDictionary*)details {
std::string callId;
callId = placeCall(std::string([accountId UTF8String]), std::string([ringId UTF8String]), [Utils dictionnaryToMap:details]);
return [NSString stringWithUTF8String:callId.c_str()];
}
......
......@@ -34,6 +34,7 @@
#import "ContactsAdapter.h"
#import "PresenceAdapter.h"
#import "VideoAdapter.h"
#import "AudioAdapter.h"
#import <CommonCrypto/CommonCrypto.h>
#import <Contacts/Contacts.h>
#import "CallsAdapter.h"
/*
* Copyright (C) 2017 Savoir-faire Linux Inc.
*
* Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
import UIKit
import Reusable
import RxSwift
class ButtonsContainerView: UIView, NibLoadable {
@IBOutlet var containerView: UIView!
@IBOutlet weak var container: UIView!
@IBOutlet weak var muteAudioButton: UIButton!
@IBOutlet weak var muteVideoButton: UIButton!
@IBOutlet weak var pauseCallButton: UIButton!
@IBOutlet weak var switchCameraButton: UIButton!
@IBOutlet weak var switchSpeakerButton: UIButton!
@IBOutlet weak var cancelButton: UIButton!
@IBOutlet weak var containerHeightConstraint: NSLayoutConstraint!
@IBOutlet weak var bottomSpaceConstraint: NSLayoutConstraint!
let disposeBag = DisposeBag()
var viewModel: ButtonsContainerViewModel? {
didSet {
self.viewModel?.observableCallOptions
.subscribe(onNext: { (callOptions) in
switch callOptions {
case .none:
self.withoutOptions()
case .optionsWithoutSpeakerphone:
self.optionsWithoutSpeaker()
case .optionsWithSpeakerphone:
self.optionsWithSpeaker()
}
}).disposed(by: self.disposeBag)
}
}
override init(frame: CGRect) {
super.init(frame: frame)
self.commonInit()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
self.commonInit()
}
override open func didMoveToWindow() {
super.didMoveToWindow()
self.cancelButton.backgroundColor = UIColor.red
if #available(iOS 11.0, *) {
guard let window = self.window else {
return
}
self.container.bottomAnchor.constraint(equalTo: window.bottomAnchor).isActive = true
}
}
func commonInit() {
Bundle.main.loadNibNamed("ButtonsContainerView", owner: self, options: nil)
addSubview(containerView)
containerView.frame = self.bounds
}
func withoutOptions() {
containerHeightConstraint.priority = UILayoutPriority(rawValue: 250.00)
bottomSpaceConstraint.priority = UILayoutPriority(rawValue: 999.00)
self.container.backgroundColor = UIColor.clear
muteAudioButton.isHidden = true
muteVideoButton.isHidden = true
pauseCallButton.isHidden = true
switchCameraButton.isHidden = true
switchSpeakerButton.isHidden = true
cancelButton.isHidden = false
}
func optionsWithSpeaker() {
containerHeightConstraint.priority = UILayoutPriority(rawValue: 999.00)
bottomSpaceConstraint.priority = UILayoutPriority(rawValue: 250.00)
self.container.backgroundColor = UIColor.black.withAlphaComponent(0.3)
muteAudioButton.isHidden = false
muteVideoButton.isHidden = false
pauseCallButton.isHidden = false
switchCameraButton.isHidden = false
switchSpeakerButton.isHidden = false
switchSpeakerButton.alpha = 1.00
switchSpeakerButton.isEnabled = true
cancelButton.isHidden = false
}
func optionsWithoutSpeaker() {
containerHeightConstraint.priority = UILayoutPriority(rawValue: 250.00)
bottomSpaceConstraint.priority = UILayoutPriority(rawValue: 999.00)
self.container.backgroundColor = UIColor.black.withAlphaComponent(0.3)
muteAudioButton.isHidden = false
muteVideoButton.isHidden = false
pauseCallButton.isHidden = false
switchCameraButton.isHidden = false
switchSpeakerButton.isHidden = false
switchSpeakerButton.alpha = 0.00
switchSpeakerButton.isEnabled = false
cancelButton.isHidden = false
}
}
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="Safe area layout guides" minToolsVersion="9.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="ButtonsContainerView" customModule="Ring" customModuleProvider="target">
<connections>
<outlet property="bottomSpaceConstraint" destination="XhM-wC-j2L" id="Kw3-nW-cVH"/>
<outlet property="cancelButton" destination="ZxT-mA-1xU" id="68q-sF-gBC"/>
<outlet property="container" destination="a9g-pf-bHy" id="6bw-CB-5qN"/>
<outlet property="containerHeightConstraint" destination="W6T-0D-HwX" id="Pdz-5B-x5j"/>
<outlet property="containerView" destination="iN0-l3-epB" id="mcP-kY-dVO"/>
<outlet property="muteAudioButton" destination="tXL-FB-O0X" id="6Bh-x3-veQ"/>
<outlet property="muteVideoButton" destination="W7F-nH-kda" id="MWK-JU-544"/>
<outlet property="pauseCallButton" destination="MPk-dB-dhR" id="W4G-AB-WFw"/>
<outlet property="switchCameraButton" destination="gfb-nb-FyB" id="FTM-2Z-u3u"/>
<outlet property="switchSpeakerButton" destination="0VH-mO-vFE" id="1Gy-a9-FAW"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB">
<rect key="frame" x="0.0" y="0.0" width="375" height="150"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="a9g-pf-bHy">
<rect key="frame" x="0.0" y="0.0" width="375" height="150"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" distribution="equalSpacing" alignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="RHx-cL-CV5">
<rect key="frame" x="12.5" y="70" width="350" height="50"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="tXL-FB-O0X">
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
<constraints>
<constraint firstAttribute="width" constant="50" id="sDn-Gs-HlM"/>
<constraint firstAttribute="height" constant="50" id="zo6-Fk-IRh"/>
</constraints>
<state key="normal" image="audio_running"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="roundedCorners" value="YES"/>
<userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
<real key="value" value="25"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="number" keyPath="borderWidth">
<real key="value" value="2"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="color" keyPath="borderColor">
<color key="value" white="1" alpha="1" colorSpace="calibratedWhite"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="W7F-nH-kda">
<rect key="frame" x="75" y="0.0" width="50" height="50"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="Pn1-SS-vPN"/>
<constraint firstAttribute="width" constant="50" id="jCp-ib-ySo"/>
</constraints>
<state key="normal" image="video_running"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="roundedCorners" value="YES"/>
<userDefinedRuntimeAttribute type="number" keyPath="borderWidth">
<real key="value" value="2"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="color" keyPath="borderColor">
<color key="value" white="1" alpha="1" colorSpace="calibratedWhite"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
<real key="value" value="25"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="0VH-mO-vFE">
<rect key="frame" x="150" y="0.0" width="50" height="50"/>
<constraints>
<constraint firstAttribute="width" constant="50" id="oso-jU-2U3"/>
<constraint firstAttribute="height" constant="50" id="qmE-To-ipl"/>
</constraints>
<state key="normal" image="disable_speakerphone"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="borderWidth">
<real key="value" value="2"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="boolean" keyPath="roundedCorners" value="YES"/>
<userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
<real key="value" value="25"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="color" keyPath="borderColor">
<color key="value" white="1" alpha="1" colorSpace="calibratedWhite"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="MPk-dB-dhR">
<rect key="frame" x="225" y="0.0" width="50" height="50"/>
<constraints>
<constraint firstAttribute="width" constant="50" id="984-Na-56G"/>
<constraint firstAttribute="height" constant="50" id="kIF-vP-ZJP"/>
</constraints>
<state key="normal" image="pause_call"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="roundedCorners" value="YES"/>
<userDefinedRuntimeAttribute type="number" keyPath="borderWidth">
<real key="value" value="2"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="color" keyPath="borderColor">
<color key="value" white="1" alpha="1" colorSpace="calibratedWhite"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
<real key="value" value="25"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="gfb-nb-FyB">
<rect key="frame" x="300" y="0.0" width="50" height="50"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="DYV-x7-KoO"/>
<constraint firstAttribute="width" constant="50" id="XYg-b8-pt8"/>
</constraints>
<state key="normal" image="switch_camera"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="roundedCorners" value="YES"/>
<userDefinedRuntimeAttribute type="number" keyPath="borderWidth">
<real key="value" value="2"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="color" keyPath="borderColor">
<color key="value" white="1" alpha="1" colorSpace="calibratedWhite"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
<real key="value" value="25"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</button>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="gaC-GZ-ftw"/>
<constraint firstAttribute="width" constant="350" id="mIk-2g-tbX"/>
</constraints>
</stackView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ZxT-mA-1xU">
<rect key="frame" x="162" y="10" width="50" height="50"/>
<constraints>
<constraint firstAttribute="width" constant="50" id="0vV-4C-odp"/>
<constraint firstAttribute="height" constant="50" id="kls-aA-2zS"/>
</constraints>
<state key="normal" image="stop_call"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="roundedCorners" value="YES"/>
<userDefinedRuntimeAttribute type="number" keyPath="borderWidth">
<real key="value" value="1"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="color" keyPath="borderColor">
<color key="value" red="1" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
<real key="value" value="25"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</button>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="ZxT-mA-1xU" firstAttribute="top" secondItem="a9g-pf-bHy" secondAttribute="top" constant="10" id="0QU-8q-IjM"/>
<constraint firstAttribute="height" priority="999" constant="150" id="W6T-0D-HwX"/>
<constraint firstAttribute="bottom" secondItem="ZxT-mA-1xU" secondAttribute="bottom" priority="250" constant="30" id="XhM-wC-j2L"/>
<constraint firstItem="RHx-cL-CV5" firstAttribute="centerX" secondItem="a9g-pf-bHy" secondAttribute="centerX" id="bmE-qo-8aJ"/>
<constraint firstAttribute="bottom" secondItem="RHx-cL-CV5" secondAttribute="bottom" constant="30" id="deA-Gc-8FZ"/>
<constraint firstItem="ZxT-mA-1xU" firstAttribute="centerX" secondItem="a9g-pf-bHy" secondAttribute="centerX" id="eV9-Wr-xF9"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="a9g-pf-bHy" firstAttribute="width" secondItem="iN0-l3-epB" secondAttribute="width" id="X3f-ZS-P0M"/>
<constraint firstItem="vUN-kp-3ea" firstAttribute="bottom" secondItem="a9g-pf-bHy" secondAttribute="bottom" id="svV-zg-XlK"/>
<constraint firstItem="a9g-pf-bHy" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="uv2-qL-KQX"/>
</constraints>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
<point key="canvasLocation" x="33.5" y="94"/>
</view>
</objects>
<resources>
<image name="audio_running" width="24" height="24"/>
<image name="disable_speakerphone" width="24" height="24"/>
<image name="pause_call" width="24" height="24"/>
<image name="stop_call" width="24" height="24"/>
<image name="switch_camera" width="25" height="25"/>
<image name="video_running" width="24" height="24"/>
</resources>
</document>
/*
* Copyright (C) 2017 Savoir-faire Linux Inc.
*
* Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
import RxSwift
enum CallOptions {
case none
case optionsWithoutSpeakerphone
case optionsWithSpeakerphone
}
class ButtonsContainerViewModel {
let callService: CallsService
let callID: String
let disposeBag = DisposeBag()
let avalaibleCallOptions = BehaviorSubject<CallOptions>(value: .none)
lazy var observableCallOptions: Observable<CallOptions> = {
return self.avalaibleCallOptions.asObservable()
}()
init(with callService: CallsService, callID: String) {
self.callService = callService
self.callID = callID
checkCallOptions()
}
private func checkCallOptions() {
let callIsActive: Observable<Bool> = {
self.callService.currentCall.filter({ call in
return call.state == .current && call.callId == self.callID
}).map({_ in
return true
})
}()
callIsActive.subscribe(onNext: { active in
if !active {
return
}
if UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.pad {
self.avalaibleCallOptions.onNext(.optionsWithoutSpeakerphone)
return
}
self.connectToSpeaker()
}).disposed(by: self.disposeBag)
}
private func connectToSpeaker() {
let speakerIsAvailable: Observable<Bool> = {
//TODO map to service
return Observable.just(true)
}()
speakerIsAvailable.subscribe(onNext: { available in
if available {
self.avalaibleCallOptions.onNext(.optionsWithSpeakerphone)
return
}
self.avalaibleCallOptions.onNext(.optionsWithoutSpeakerphone)
}).disposed(by: self.disposeBag)
}
}
......@@ -33,31 +33,31 @@
<blurEffect style="light"/>
</visualEffectView>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_contact_picture" translatesAutoresizingMaskIntoConstraints="NO" id="fnt-PQ-Q6P">
<rect key="frame" x="138" y="64" width="100" height="100"/>
<rect key="frame" x="113" y="150" width="150" height="150"/>
<constraints>
<constraint firstAttribute="width" constant="100" id="Miw-Nd-4Fa"/>
<constraint firstAttribute="height" constant="100" id="V9c-7W-Frv"/>
<constraint firstAttribute="width" constant="150" id="Miw-Nd-4Fa"/>
<constraint firstAttribute="height" constant="150" id="V9c-7W-Frv"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
<real key="value" value="50"/>
<real key="value" value="75"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="73Y-N1-Yga">
<rect key="frame" x="188" y="172" width="0.0" height="0.0"/>
<rect key="frame" x="188" y="308" width="0.0" height="0.0"/>
<fontDescription key="fontDescription" type="system" pointSize="26"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="SdV-jx-Mla">
<rect key="frame" x="188" y="579" width="0.0" height="0.0"/>
<rect key="frame" x="188" y="487" width="0.0" height="0.0"/>
<fontDescription key="fontDescription" type="system" pointSize="20"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zMN-6z-uXT">
<rect key="frame" x="188" y="188" width="0.0" height="0.0"/>
<rect key="frame" x="188" y="324" width="0.0" height="0.0"/>
<fontDescription key="fontDescription" type="system" pointSize="20"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
......@@ -69,6 +69,13 @@
<rect key="