diff --git a/Ring/Ring/Bridging/CallsAdapter.h b/Ring/Ring/Bridging/CallsAdapter.h index e433cd223df4a9cb3d3b0848e9608a02d6d212f5..f4bea53db12a080063a8e502f27d9d53b9ccdd27 100644 --- a/Ring/Ring/Bridging/CallsAdapter.h +++ b/Ring/Ring/Bridging/CallsAdapter.h @@ -55,4 +55,5 @@ - (void)setConferenceModerator:(NSString*)participantId forConference:(NSString*)conferenceId accountId:(NSString*)accountId active:(BOOL)isActive; - (void)muteConferenceParticipant:(NSString*)participantId forConference:(NSString*)conferenceId accountId:(NSString*)accountId active:(BOOL)isActive; - (void)hangupConferenceParticipant:(NSString*)participantId forConference:(NSString*)conferenceId accountId:(NSString*)accountId; +-(void)setHandRaised:(NSString*)participantId forConference:(NSString*)conferenceId accountId:(NSString*)accountId state:(BOOL)state; @end diff --git a/Ring/Ring/Bridging/CallsAdapter.mm b/Ring/Ring/Bridging/CallsAdapter.mm index 949a7a2c593aa2343f954d9eb889bb2a7feb72c7..25582283a256d090d9cd63a7ec94ffa12292476e 100644 --- a/Ring/Ring/Bridging/CallsAdapter.mm +++ b/Ring/Ring/Bridging/CallsAdapter.mm @@ -285,7 +285,9 @@ static id <CallsAdapterDelegate> _delegate; - (void)hangupConferenceParticipant:(NSString*)participantId forConference:(NSString*)conferenceId accountId:(NSString*)accountId { hangupParticipant(std::string([accountId UTF8String]), std::string([conferenceId UTF8String]), std::string([participantId UTF8String])); } - +-(void)setHandRaised:(NSString*)participantId forConference:(NSString*)conferenceId accountId:(NSString*)accountId state:(BOOL)state { + raiseParticipantHand(std::string([accountId UTF8String]), std::string([conferenceId UTF8String]), std::string([participantId UTF8String]), state); +} #pragma mark AccountAdapterDelegate diff --git a/Ring/Ring/Calls/ButtonsContainerView.swift b/Ring/Ring/Calls/ButtonsContainerView.swift index ef54b18d500cb47dff81e70089fed4dc152c6c19..4c4eb6cbc39eb491ae7e48700360c538162ef3bc 100644 --- a/Ring/Ring/Calls/ButtonsContainerView.swift +++ b/Ring/Ring/Calls/ButtonsContainerView.swift @@ -50,6 +50,7 @@ class ButtonsContainerView: UIView, NibLoadable, UIScrollViewDelegate { var switchCameraButton: UIButton! var switchSpeakerButton: UIButton! var addParticipantButton: UIButton! + var raiseHandButton: UIButton! private let disposeBag = DisposeBag() var callViewMode: CallViewMode = .audio @@ -103,6 +104,7 @@ class ButtonsContainerView: UIView, NibLoadable, UIScrollViewDelegate { switchCameraButton = configureButton(image: UIImage(asset: Asset.switchCamera)) switchSpeakerButton = configureButton(image: UIImage(asset: Asset.enableSpeakerphone)) addParticipantButton = configureButton(image: UIImage(asset: Asset.addPerson)) + raiseHandButton = configureButton(image: UIImage(asset: Asset.raiseHand)?.withRenderingMode(.alwaysTemplate)) pageControl.addTarget(self, action: #selector(changePage), for: UIControl.Event.valueChanged) } @@ -138,6 +140,7 @@ class ButtonsContainerView: UIView, NibLoadable, UIScrollViewDelegate { cancelButton.isHidden = true switchSpeakerButton.isEnabled = enable let isSip = self.viewModel?.isSipCall ?? false + let isConference = self.viewModel?.isConference ?? false let audioOnly = self.callViewMode == .audio var havePages = false if isSip { @@ -151,7 +154,7 @@ class ButtonsContainerView: UIView, NibLoadable, UIScrollViewDelegate { } else if audioOnly { let screenRect = UIScreen.main.bounds let screenWidth: CGFloat = screenRect.size.width - let buttonsWidth: CGFloat = 6 * 50 + 30 * 5 + let buttonsWidth: CGFloat = 7 * 50 + 30 * 6 havePages = screenWidth < buttonsWidth firstPageStackView.removeSubviews() secondPageStackView.removeSubviews() @@ -162,14 +165,20 @@ class ButtonsContainerView: UIView, NibLoadable, UIScrollViewDelegate { if havePages { secondPageStackView.addArrangedSubview(muteAudioButton) secondPageStackView.addArrangedSubview(muteVideoButton) + if isConference { + secondPageStackView.addArrangedSubview(raiseHandButton) + } } else { firstPageStackView.addArrangedSubview(muteAudioButton) firstPageStackView.addArrangedSubview(muteVideoButton) + if isConference { + firstPageStackView.addArrangedSubview(raiseHandButton) + } } } else { let screenRect = UIScreen.main.bounds let screenWidth: CGFloat = screenRect.size.width - let buttonsWidth: CGFloat = 7 * 50 + 30 * 6 // 540 + let buttonsWidth: CGFloat = 8 * 50 + 30 * 7 // 540 havePages = screenWidth < buttonsWidth firstPageStackView.removeSubviews() secondPageStackView.removeSubviews() @@ -181,9 +190,15 @@ class ButtonsContainerView: UIView, NibLoadable, UIScrollViewDelegate { if havePages { secondPageStackView.addArrangedSubview(muteAudioButton) secondPageStackView.addArrangedSubview(muteVideoButton) + if isConference { + secondPageStackView.addArrangedSubview(raiseHandButton) + } } else { firstPageStackView.addArrangedSubview(muteAudioButton) firstPageStackView.addArrangedSubview(muteVideoButton) + if isConference { + firstPageStackView.addArrangedSubview(raiseHandButton) + } } } pageControl.isHidden = !havePages @@ -228,6 +243,7 @@ class ButtonsContainerView: UIView, NibLoadable, UIScrollViewDelegate { dialpadButton.borderColor = UIColor.gray switchSpeakerButton.tintColor = UIColor.gray switchSpeakerButton.borderColor = UIColor.gray + raiseHandButton.tintColor = UIColor.gray } func updateColorForVideo() { @@ -245,6 +261,7 @@ class ButtonsContainerView: UIView, NibLoadable, UIScrollViewDelegate { muteVideoButton.tintColor = UIColor.white muteVideoButton.borderColor = UIColor.white switchCameraButton.tintColor = UIColor.white + raiseHandButton.tintColor = UIColor.white } @objc diff --git a/Ring/Ring/Calls/ButtonsContainerView.xib b/Ring/Ring/Calls/ButtonsContainerView.xib index 885e350c58acd80d2da39c0af65e61708133f62f..c89215fc6548e0c523ce1bc7d46d24590adfb900 100644 --- a/Ring/Ring/Calls/ButtonsContainerView.xib +++ b/Ring/Ring/Calls/ButtonsContainerView.xib @@ -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" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> <device id="retina4_7" orientation="portrait" appearance="light"/> <dependencies> <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> </dependencies> @@ -127,7 +127,7 @@ </userDefinedRuntimeAttributes> </button> <pageControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" defersCurrentPageDisplay="YES" numberOfPages="2" translatesAutoresizingMaskIntoConstraints="NO" id="p1T-pp-N9T"> - <rect key="frame" x="198.5" y="172.5" width="103.5" height="27.5"/> + <rect key="frame" x="199" y="172.5" width="102.5" height="27.5"/> <color key="pageIndicatorTintColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> </pageControl> </subviews> diff --git a/Ring/Ring/Calls/CallViewController.swift b/Ring/Ring/Calls/CallViewController.swift index c1a6690f58d0a2f7429260e88a121c3c2f17fafc..713b9754dea16bc9327aa2629d5d4d1ce650d25b 100644 --- a/Ring/Ring/Calls/CallViewController.swift +++ b/Ring/Ring/Calls/CallViewController.swift @@ -148,7 +148,8 @@ class CallViewController: UIViewController, StoryboardBased, ViewModelBased, Con guard let self = self, updated else { return } self.updateconferenceLayoutSize() let participants = self.viewModel.getConferenceParticipants() - self.conferenceLayout.setParticipants(participants: participants) + self.conferenceLayout.setParticipants(participants: participants, isCurrentModerator: self.viewModel.isCurrentModerator() || self.viewModel.isHostCall ) + guard let unwrapParticipants = participants, self.viewModel.isCurrentModerator(), !self.viewModel.isHostCall else { return } self.conferenceCalls.arrangedSubviews.forEach({ (view) in view.removeFromSuperview() @@ -295,6 +296,11 @@ class CallViewController: UIViewController, StoryboardBased, ViewModelBased, Con self.viewModel.showContactPickerVC() }) .disposed(by: self.disposeBag) + self.buttonsContainer.raiseHandButton.rx.tap + .subscribe(onNext: { [weak self] in + self?.viewModel.togleRaiseHand() + }) + .disposed(by: self.disposeBag) // Data bindings self.viewModel.videoButtonState .observe(on: MainScheduler.instance) @@ -906,6 +912,10 @@ extension CallViewController: ConferenceParticipantViewDelegate { self?.removeConferenceParticipantMenu() self?.viewModel.muteParticipant(participantId: participantId, active: !isAudioMuted) } + menuView.addLowerHandAction { [weak self] in + self?.removeConferenceParticipantMenu() + self?.viewModel.lowerHandFor(participantId: participantId) + } let point = conferenceCallsScrolView.convert(menuView.frame.origin, to: self.view) let offset = self.view.frame.size.width - point.x - menuView.frame.size.width diff --git a/Ring/Ring/Calls/CallViewModel.swift b/Ring/Ring/Calls/CallViewModel.swift index e1d50071e3a7fcb01721e2ea9b636c4f91a32fc1..85650a9cb9f1c7ea6384a0321c3e7125dcec83b1 100644 --- a/Ring/Ring/Calls/CallViewModel.swift +++ b/Ring/Ring/Calls/CallViewModel.swift @@ -753,11 +753,12 @@ extension CallViewModel { return self.callService.isModerator(participantId: account.jamiId, inConference: self.rendererId) } func getItemsForConferenceMenu(participantId: String, callId: String) -> [MenuItem] { + guard let participant = self.getConferencePartisipant(participantId: participantId) else { return [MenuItem]() } let conference = self.callService.call(callID: self.rendererId) let active = self.callService.isParticipant(participantURI: participantId, activeIn: self.rendererId, accountId: conference?.accountId ?? "") // menu for local call if self.isLocalCall(participantId: participantId) || participantId.isEmpty { - return menuItemsManager.getMenuItemsForLocalCall(conference: conference, active: active) + return menuItemsManager.getMenuItemsForLocalCall(conference: conference, active: active, isHandRised: participant.isHandRaised) } let isModerator = self.isCurrentModerator() var role = RoleInCall.regular @@ -768,6 +769,19 @@ extension CallViewModel { role = RoleInCall.moderator } let participantCall = isModerator ? call : self.callService.call(callID: callId) - return menuItemsManager.getMenuItemsFor(call: participantCall, isHost: callIsHost, conference: conference, active: active, role: role) + return menuItemsManager.getMenuItemsFor(call: participantCall, isHost: callIsHost, conference: conference, active: active, role: role, isHandRised: participant.isHandRaised) + } + + func lowerHandFor(participantId: String) { + self.callService.setRaiseHand(confId: self.rendererId, participantId: participantId.filterOutHost(), state: false) + } + + func togleRaiseHand() { + guard let account = self.accountService.currentAccount else { return } + guard let partisipant = self.getConferenceParticipants()?.filter({ participant in + participant.displayName == L10n.Account.me + }).first else { return } + let state = !partisipant.isHandRaised + self.callService.setRaiseHand(confId: self.rendererId, participantId: account.jamiId, state: state) } } diff --git a/Ring/Ring/Calls/Conference/ConferenceLayout.swift b/Ring/Ring/Calls/Conference/ConferenceLayout.swift index 1568ae7c3fd443245a25a72b4a83b41dbc8cc078..1bd473b580858d510a8dcd1b143a53b0b68034f4 100644 --- a/Ring/Ring/Calls/Conference/ConferenceLayout.swift +++ b/Ring/Ring/Calls/Conference/ConferenceLayout.swift @@ -27,9 +27,11 @@ class ConferenceLayout: UIView { private var participants: [ConferenceParticipant] = [ConferenceParticipant]() private let textSize: CGFloat = 16 private let labelHight: CGFloat = 30 + private let controlSize: CGFloat = 25 private let margin: CGFloat = 15 private let minWidth: CGFloat = 50 private let conferenceLayoutHelper: ConferenceLayoutHelper = ConferenceLayoutHelper() + private var isCurrentModerator: Bool = false private let disposeBag = DisposeBag() func setUpWithVideoSize(size: CGSize) { @@ -46,12 +48,13 @@ class ConferenceLayout: UIView { self.updateViewSize() } - func setParticipants(participants: [ConferenceParticipant]?) { + func setParticipants(participants: [ConferenceParticipant]?, isCurrentModerator: Bool) { if let participants = participants { self.participants = participants } else { self.participants.removeAll() } + self.isCurrentModerator = isCurrentModerator self.layoutParticipantsViews() } @@ -81,6 +84,7 @@ class ConferenceLayout: UIView { let origX: CGFloat = participant.originX * widthRatio let origY: CGFloat = participant.originY * heightRatio let width: CGFloat = participant.width * widthRatio + let height: CGFloat = participant.height * heightRatio // do not add labels when view width is too small if width < minWidth { return } let background = UIView(frame: CGRect(x: origX, y: origY, width: width, height: self.labelHight)) @@ -95,5 +99,16 @@ class ConferenceLayout: UIView { label.font = label.font.withSize(self.textSize) self.addSubview(background) self.addSubview(label) + if !participant.isHandRaised || !self.isCurrentModerator { + return + } + let raisedHandImage = UIButton(frame: CGRect(x: origX + width - self.controlSize, y: origY + height - self.controlSize, width: self.controlSize, height: self.controlSize)) + let image = UIImage(asset: Asset.raiseHand)?.withRenderingMode(.alwaysTemplate) + raisedHandImage.setImage(image, for: .normal) + raisedHandImage.tintColor = UIColor.white + raisedHandImage.backgroundColor = UIColor.conferenceRaiseHand + raisedHandImage.layer.cornerRadius = 4 + raisedHandImage.layer.maskedCorners = [.layerMinXMinYCorner] + self.addSubview(raisedHandImage) } } diff --git a/Ring/Ring/Calls/Conference/ConferenceMenuItemsManager.swift b/Ring/Ring/Calls/Conference/ConferenceMenuItemsManager.swift index 3d6dd63282b629463162739b41509a652df8fcca..7c0926562d716eff0ae14a47a455811b531941e1 100644 --- a/Ring/Ring/Calls/Conference/ConferenceMenuItemsManager.swift +++ b/Ring/Ring/Calls/Conference/ConferenceMenuItemsManager.swift @@ -23,7 +23,7 @@ enum RoleInCall { case regular } class ConferenceMenuItemsManager { - func getMenuItemsForLocalCall(conference: CallModel?, active: Bool?) -> [MenuItem] { + func getMenuItemsForLocalCall(conference: CallModel?, active: Bool?, isHandRised: Bool) -> [MenuItem] { var menu = [MenuItem]() menu.append(.name) guard let conference = conference else { @@ -32,6 +32,9 @@ class ConferenceMenuItemsManager { guard let active = active else { return menu } + if isHandRised { + menu.append(.lowerHand) + } switch conference.layout { case .grid: menu.append(.maximize) @@ -51,7 +54,8 @@ class ConferenceMenuItemsManager { return menu } - func getMenuItemsFor(call: CallModel?, isHost: Bool, conference: CallModel?, active: Bool?, role: RoleInCall) -> [MenuItem] { + // swiftlint:disable cyclomatic_complexity + func getMenuItemsFor(call: CallModel?, isHost: Bool, conference: CallModel?, active: Bool?, role: RoleInCall, isHandRised: Bool) -> [MenuItem] { var menu = [MenuItem]() menu.append(.name) guard let conference = conference, @@ -65,6 +69,9 @@ class ConferenceMenuItemsManager { guard let active = active else { return menu } + if isHandRised { + menu.append(.lowerHand) + } switch conference.layout { case .grid: menu.append(.maximize) diff --git a/Ring/Ring/Calls/views/ConferenceActionsMenu.swift b/Ring/Ring/Calls/views/ConferenceActionsMenu.swift index 8839fff803be7dcc9dd4afd1544e295102dacc20..5f90904d9eceb0a30002f51539f9bf4ec6dfe85f 100644 --- a/Ring/Ring/Calls/views/ConferenceActionsMenu.swift +++ b/Ring/Ring/Calls/views/ConferenceActionsMenu.swift @@ -28,6 +28,7 @@ enum MenuItem { case maximize case setModerator case muteAudio + case lowerHand } class ConferenceActionMenu: UIView { @@ -42,6 +43,7 @@ class ConferenceActionMenu: UIView { private var minimizeButton: UIButton? private var setModeratorButton: UIButton? private var muteAudioButton: UIButton? + private var lowerHandButton: UIButton? private let disposeBag = DisposeBag() private var muteLabelText: String = "" private var moderatorLabelText: String = "" @@ -81,6 +83,8 @@ class ConferenceActionMenu: UIView { self.addHangUpButton(positionY: positionY) case .name: break + case .lowerHand: + self.addLowerHandButton(positionY: positionY) } } @@ -91,6 +95,13 @@ class ConferenceActionMenu: UIView { .disposed(by: self.disposeBag) } + func addLowerHandAction(lowerHand: @escaping (() -> Void)) { + guard let button = lowerHandButton else { return } + button.rx.tap + .subscribe(onNext: { lowerHand() }) + .disposed(by: self.disposeBag) + } + func addMaximizeAction(maximize: @escaping (() -> Void)) { guard let button = maximizeButton else { return } button.rx.tap @@ -149,6 +160,17 @@ class ConferenceActionMenu: UIView { self.addSubview(self.hangUpButton!) } + private func addLowerHandButton(positionY: CGFloat) { + let lowerHandLabel = UILabel(frame: CGRect(x: marginX, y: positionY, width: menuItemWidth, height: menuItemHight)) + lowerHandLabel.font = lowerHandLabel.font.withSize(self.textSize) + lowerHandLabel.text = L10n.Calls.lowerHand + lowerHandLabel.sizeToFit() + lowerHandLabel.textAlignment = .center + self.lowerHandButton = UIButton(frame: lowerHandLabel.frame) + self.addSubview(lowerHandLabel) + self.addSubview(self.lowerHandButton!) + } + private func addMaximizeButton(positionY: CGFloat) { let maximizeLabel = UILabel(frame: CGRect(x: marginX, y: positionY, width: menuItemWidth, height: menuItemHight)) maximizeLabel.font = maximizeLabel.font.withSize(self.textSize) diff --git a/Ring/Ring/Constants/Generated/Images.swift b/Ring/Ring/Constants/Generated/Images.swift index a0ee21a45b57efba48cbb549ff047afd022099a5..fbff4f1b1224e1c6151831cda21309637df2abaa 100644 --- a/Ring/Ring/Constants/Generated/Images.swift +++ b/Ring/Ring/Constants/Generated/Images.swift @@ -66,6 +66,7 @@ internal enum Asset { internal static let phoneBook = ImageAsset(name: "phone_book") internal static let qrCode = ImageAsset(name: "qr_code") internal static let qrCodeScan = ImageAsset(name: "qr_code_scan") + internal static let raiseHand = ImageAsset(name: "raise_hand") internal static let revokeDevice = ImageAsset(name: "revoke_device") internal static let ringLogo = ImageAsset(name: "ring_logo") internal static let rowSelected = ColorAsset(name: "row_selected") diff --git a/Ring/Ring/Constants/Generated/Strings.swift b/Ring/Ring/Constants/Generated/Strings.swift index 230c887dbeac4ab7ece6e64768d06deeaaac6c37..030cc5f89ee4654a400b98712d224f29b567a77a 100644 --- a/Ring/Ring/Constants/Generated/Strings.swift +++ b/Ring/Ring/Constants/Generated/Strings.swift @@ -293,6 +293,8 @@ internal enum L10n { internal static let haghUp = L10n.tr("Localizable", "calls.haghUp") /// wants to talk to you internal static let incomingCallInfo = L10n.tr("Localizable", "calls.incomingCallInfo") + /// lower hand + internal static let lowerHand = L10n.tr("Localizable", "calls.lowerHand") /// maximize internal static let maximize = L10n.tr("Localizable", "calls.maximize") /// minimize diff --git a/Ring/Ring/Extensions/UIColor+Ring.swift b/Ring/Ring/Extensions/UIColor+Ring.swift index b5a1d0421103b2f4780e3179634bd76d8210e7c9..1eb709232db2c3a5159d1360a0d79d17e8ecf4ed 100644 --- a/Ring/Ring/Extensions/UIColor+Ring.swift +++ b/Ring/Ring/Extensions/UIColor+Ring.swift @@ -74,6 +74,7 @@ extension UIColor { } static let jamiMain = UIColor(hex: 0x3F6DA7, alpha: 1.0) + static let conferenceRaiseHand = UIColor(red: 0, green: 184, blue: 255, alpha: 1.0) static let jamiSecondary = UIColor(hex: 0x1F4971, alpha: 1.0) static let jamiButtonLight = UIColor(hex: 0x285F97, alpha: 1.0) static let jamiButtonDark = UIColor(hex: 0x0F2643, alpha: 1.0) diff --git a/Ring/Ring/Models/ConferenceParticipant.swift b/Ring/Ring/Models/ConferenceParticipant.swift index 1b5a54f04c4bd05bdc8e654d0acdb1ed5bf82a85..0f5f7fc40aa839d27f95cb00b21c8be5d0fda8d8 100644 --- a/Ring/Ring/Models/ConferenceParticipant.swift +++ b/Ring/Ring/Models/ConferenceParticipant.swift @@ -30,6 +30,7 @@ class ConferenceParticipant { var isAudioLocalyMuted: Bool = false var isAudioMuted: Bool = false var isVideoMuted: Bool = false + var isHandRaised: Bool = false init (info: [String: String], onlyURIAndActive: Bool) { self.uri = info["uri"] @@ -63,5 +64,8 @@ class ConferenceParticipant { if let isModerator = info["isModerator"] { self.isModerator = isModerator.boolValue } + if let isHandRaised = info["handRaised"] { + self.isHandRaised = isHandRaised.boolValue + } } } diff --git a/Ring/Ring/Resources/Images.xcassets/raise_hand.imageset/Contents.json b/Ring/Ring/Resources/Images.xcassets/raise_hand.imageset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..8621eaf21a5afaf2423af31671b8603df02bff88 --- /dev/null +++ b/Ring/Ring/Resources/Images.xcassets/raise_hand.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "hand_black.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "hand_black_50.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "hand_black_75.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Ring/Ring/Resources/Images.xcassets/raise_hand.imageset/hand_black.png b/Ring/Ring/Resources/Images.xcassets/raise_hand.imageset/hand_black.png new file mode 100644 index 0000000000000000000000000000000000000000..1f651af3cbc4e55abe0916603e2b9bc7d17c6eae Binary files /dev/null and b/Ring/Ring/Resources/Images.xcassets/raise_hand.imageset/hand_black.png differ diff --git a/Ring/Ring/Resources/Images.xcassets/raise_hand.imageset/hand_black_50.png b/Ring/Ring/Resources/Images.xcassets/raise_hand.imageset/hand_black_50.png new file mode 100644 index 0000000000000000000000000000000000000000..dd81adaf8b645a9ce753052c09076be0715b69a4 Binary files /dev/null and b/Ring/Ring/Resources/Images.xcassets/raise_hand.imageset/hand_black_50.png differ diff --git a/Ring/Ring/Resources/Images.xcassets/raise_hand.imageset/hand_black_75.png b/Ring/Ring/Resources/Images.xcassets/raise_hand.imageset/hand_black_75.png new file mode 100644 index 0000000000000000000000000000000000000000..933a60b6a2fc0a04637ff4058c17ca7c9f60d497 Binary files /dev/null and b/Ring/Ring/Resources/Images.xcassets/raise_hand.imageset/hand_black_75.png differ diff --git a/Ring/Ring/Resources/en.lproj/Localizable.strings b/Ring/Ring/Resources/en.lproj/Localizable.strings index 50129525af630595d623a644307605a185ad8154..d641d48361188744d3178bd8383f389db7cf5346 100644 --- a/Ring/Ring/Resources/en.lproj/Localizable.strings +++ b/Ring/Ring/Resources/en.lproj/Localizable.strings @@ -199,6 +199,7 @@ "calls.removeModerator" = "unset moderator"; "calls.muteAudio" = "mute audio"; "calls.unmuteAudio" = "unmute audio"; +"calls.lowerHand" = "lower hand"; //Account Page "accountPage.devicesListHeader" = "Devices"; diff --git a/Ring/Ring/Services/CallsService.swift b/Ring/Ring/Services/CallsService.swift index e3ec4eb9a166090d52895e8ae245bc8700658a54..818c7b26f2761355f75e302d3b20d8b4fc7e0ff8 100644 --- a/Ring/Ring/Services/CallsService.swift +++ b/Ring/Ring/Services/CallsService.swift @@ -819,4 +819,10 @@ class CallsService: CallsAdapterDelegate { guard let conference = call(callID: confId) else { return } self.callsAdapter.hangupConferenceParticipant(participantId, forConference: confId, accountId: conference.accountId) } + + func setRaiseHand(confId: String, participantId: String, state: Bool) { + guard let conference = call(callID: confId) else { return } + self.callsAdapter.setHandRaised(participantId, forConference: confId, accountId: conference.accountId, state: state) + } + }