mirror of
https://github.com/vector-im/element-ios.git
synced 2024-09-28 23:32:41 +00:00
Audio route menu and item views
This commit is contained in:
parent
4807445efc
commit
592ea5bf8d
3 changed files with 265 additions and 0 deletions
104
Riot/Modules/Call/Views/CallAudioRouteMenuView.swift
Normal file
104
Riot/Modules/Call/Views/CallAudioRouteMenuView.swift
Normal file
|
@ -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
|
||||
}
|
||||
|
||||
}
|
83
Riot/Modules/Call/Views/CallAudioRouteView.swift
Normal file
83
Riot/Modules/Call/Views/CallAudioRouteView.swift
Normal file
|
@ -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
|
||||
}
|
||||
|
||||
}
|
78
Riot/Modules/Call/Views/CallAudioRouteView.xib
Normal file
78
Riot/Modules/Call/Views/CallAudioRouteView.xib
Normal file
|
@ -0,0 +1,78 @@
|
|||
<?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" useTraitCollections="YES" useSafeAreas="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="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<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="CallAudioRouteView" customModule="Riot" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="bottomLineView" destination="LBR-tU-UxC" id="1tu-bV-izu"/>
|
||||
<outlet property="iconImageView" destination="jLA-5c-Zt1" id="7NK-9l-u5P"/>
|
||||
<outlet property="selectedIconImageView" destination="RKb-uU-qR6" id="F09-lz-bN4"/>
|
||||
<outlet property="titleLabel" destination="diN-eC-pOA" id="FII-J4-ZnN"/>
|
||||
</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="414" height="61"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="LBR-tU-UxC">
|
||||
<rect key="frame" x="0.0" y="60" width="414" height="1"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="1" id="m9B-0N-ugj"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="jLA-5c-Zt1">
|
||||
<rect key="frame" x="12" y="18.5" width="24" height="24"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="24" id="TXK-r1-bOI"/>
|
||||
<constraint firstAttribute="width" constant="24" id="Whn-3a-Cvz"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="checkmark" translatesAutoresizingMaskIntoConstraints="NO" id="RKb-uU-qR6">
|
||||
<rect key="frame" x="374" y="18.5" width="24" height="24"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="24" id="KkM-xG-8Es"/>
|
||||
<constraint firstAttribute="height" constant="24" id="UGP-Up-jBV"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="diN-eC-pOA">
|
||||
<rect key="frame" x="44" y="8" width="322" height="44"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="19"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="LBR-tU-UxC" firstAttribute="trailing" secondItem="vUN-kp-3ea" secondAttribute="trailing" id="3yO-XI-bnL"/>
|
||||
<constraint firstItem="LBR-tU-UxC" firstAttribute="bottom" secondItem="vUN-kp-3ea" secondAttribute="bottom" id="85A-Hu-UvO"/>
|
||||
<constraint firstItem="LBR-tU-UxC" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="Efb-YZ-r7n"/>
|
||||
<constraint firstItem="diN-eC-pOA" firstAttribute="leading" secondItem="jLA-5c-Zt1" secondAttribute="trailing" constant="8" id="InE-KY-BCr"/>
|
||||
<constraint firstItem="LBR-tU-UxC" firstAttribute="top" secondItem="diN-eC-pOA" secondAttribute="bottom" constant="8" id="JgK-T8-UEb"/>
|
||||
<constraint firstItem="jLA-5c-Zt1" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" id="Lzk-Ok-frf"/>
|
||||
<constraint firstItem="diN-eC-pOA" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="8" id="lT9-3y-xRk"/>
|
||||
<constraint firstItem="jLA-5c-Zt1" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="12" id="mqI-PE-ePl"/>
|
||||
<constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="RKb-uU-qR6" secondAttribute="trailing" constant="16" id="p6S-dx-14q"/>
|
||||
<constraint firstItem="RKb-uU-qR6" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" id="rd7-Kc-7eL"/>
|
||||
<constraint firstItem="RKb-uU-qR6" firstAttribute="leading" secondItem="diN-eC-pOA" secondAttribute="trailing" constant="8" id="szb-mp-nYy"/>
|
||||
</constraints>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<point key="canvasLocation" x="5.7971014492753632" y="-147.65625"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="checkmark" width="16" height="12.5"/>
|
||||
<systemColor name="systemBackgroundColor">
|
||||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</systemColor>
|
||||
</resources>
|
||||
</document>
|
Loading…
Reference in a new issue