From 592ea5bf8dc65027b60e13c25dcc2f2477eed284 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 28 Jun 2021 02:21:40 +0300 Subject: [PATCH] Audio route menu and item views --- .../Call/Views/CallAudioRouteMenuView.swift | 104 ++++++++++++++++++ .../Call/Views/CallAudioRouteView.swift | 83 ++++++++++++++ .../Modules/Call/Views/CallAudioRouteView.xib | 78 +++++++++++++ 3 files changed, 265 insertions(+) create mode 100644 Riot/Modules/Call/Views/CallAudioRouteMenuView.swift create mode 100644 Riot/Modules/Call/Views/CallAudioRouteView.swift create mode 100644 Riot/Modules/Call/Views/CallAudioRouteView.xib diff --git a/Riot/Modules/Call/Views/CallAudioRouteMenuView.swift b/Riot/Modules/Call/Views/CallAudioRouteMenuView.swift new file mode 100644 index 000000000..6cce1a26e --- /dev/null +++ b/Riot/Modules/Call/Views/CallAudioRouteMenuView.swift @@ -0,0 +1,104 @@ +// +// Copyright 2021 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +@objc +protocol CallAudioRouteMenuViewDelegate: AnyObject { + func callAudioRouteMenuView(_ view: CallAudioRouteMenuView, didSelectRoute route: MXAudioOutputRoute) +} + +@objcMembers +class CallAudioRouteMenuView: UIStackView { + + private enum Constants { + static let routeHeight: CGFloat = 62 + } + + let routes: [MXAudioOutputRoute] + let currentRoute: MXAudioOutputRoute? + + private var theme: Theme = DefaultTheme() + + weak var delegate: CallAudioRouteMenuViewDelegate? + + init(withRoutes routes: [MXAudioOutputRoute], + currentRoute: MXAudioOutputRoute?) { + self.routes = routes + self.currentRoute = currentRoute + super.init(frame: .zero) + setup() + } + + required init(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setup() { + axis = .vertical + alignment = .fill + distribution = .fillEqually + + for (index, route) in routes.enumerated() { + let routeView = CallAudioRouteView(withRoute: route, + isSelected: route == currentRoute, + isBottomLineHidden: index == routes.count - 1) + + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(routeTapped(_:))) + routeView.addGestureRecognizer(tapGesture) + + addArrangedSubview(routeView) + } + + update(theme: theme) + } + + @objc + private func routeTapped(_ sender: UITapGestureRecognizer) { + if let routeView = sender.view as? CallAudioRouteView { + delegate?.callAudioRouteMenuView(self, didSelectRoute: routeView.route) + } + } + +} + +extension CallAudioRouteMenuView: Themable { + + func update(theme: Theme) { + self.theme = DefaultTheme() + + backgroundColor = self.theme.colors.navigation + + for view in arrangedSubviews { + if let view = view as? Themable { + view.update(theme: self.theme) + } + } + } + +} + +extension CallAudioRouteMenuView: SlidingModalPresentable { + + func allowsDismissOnBackgroundTap() -> Bool { + return true + } + + func layoutHeightFittingWidth(_ width: CGFloat) -> CGFloat { + return CGFloat(routes.count) * Constants.routeHeight + } + +} diff --git a/Riot/Modules/Call/Views/CallAudioRouteView.swift b/Riot/Modules/Call/Views/CallAudioRouteView.swift new file mode 100644 index 000000000..d4bab4a90 --- /dev/null +++ b/Riot/Modules/Call/Views/CallAudioRouteView.swift @@ -0,0 +1,83 @@ +// +// Copyright 2021 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import Reusable + +class CallAudioRouteView: UIView { + + @IBOutlet private weak var iconImageView: UIImageView! + @IBOutlet private weak var titleLabel: UILabel! + @IBOutlet private weak var selectedIconImageView: UIImageView! + @IBOutlet private weak var bottomLineView: UIView! + + let route: MXAudioOutputRoute + private let isSelected: Bool + private let isBottomLineHidden: Bool + + private var theme: Theme = ThemeService.shared().theme + + init(withRoute route: MXAudioOutputRoute, + isSelected: Bool, + isBottomLineHidden: Bool = false) { + self.route = route + self.isSelected = isSelected + self.isBottomLineHidden = isBottomLineHidden + super.init(frame: .zero) + loadNibContent() + setup() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setup() { + selectedIconImageView.isHidden = !isSelected + bottomLineView.isHidden = isBottomLineHidden + + switch route.routeType { + case .builtIn: + iconImageView.image = Asset.Images.callAudioRouteBuiltin.image + titleLabel.text = route.name + case .loudSpeakers: + iconImageView.image = Asset.Images.callAudioRouteSpeakers.image + titleLabel.text = Bundle.mxk_localizedString(forKey: "call_more_actions_audio_use_device") + case .externalWired, .externalBluetooth, .externalCar: + iconImageView.image = Asset.Images.callAudioRouteHeadphones.image + titleLabel.text = route.name + } + + update(theme: theme) + } + +} + +extension CallAudioRouteView: NibOwnerLoadable {} + +extension CallAudioRouteView: Themable { + + func update(theme: Theme) { + self.theme = theme + + backgroundColor = theme.colors.navigation + iconImageView.tintColor = theme.colors.secondaryContent + titleLabel.textColor = theme.colors.primaryContent + selectedIconImageView.tintColor = theme.colors.primaryContent + bottomLineView.backgroundColor = theme.colors.secondaryContent + } + +} diff --git a/Riot/Modules/Call/Views/CallAudioRouteView.xib b/Riot/Modules/Call/Views/CallAudioRouteView.xib new file mode 100644 index 000000000..97813ec9c --- /dev/null +++ b/Riot/Modules/Call/Views/CallAudioRouteView.xib @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +