Merge commit 'ace42be63764c1f1aec82d6e3448ca8980adc784' into feature/3746_merge_element_1.9.10

# Conflicts:
#	Config/AppConfiguration.swift
#	Config/AppVersion.xcconfig
#	Podfile.lock
#	Riot/Modules/Application/AppCoordinator.swift
#	Riot/Modules/Common/Avatar/AvatarView.swift
#	Riot/Modules/Room/TimelineCells/Styles/Bubble/BubbleRoomTimelineCellProvider.m
#	Riot/Modules/Room/TimelineCells/Styles/Plain/PlainRoomTimelineCellProvider.m
#	Riot/Modules/Settings/Security/SecurityViewController.m
#	Riot/Modules/Settings/SettingsViewController.m
#	Riot/Modules/TabBar/TabBarCoordinator.swift
#	Riot/target.yml
#	fastlane/Fastfile
#	project.yml
This commit is contained in:
Frank Rotermund
2022-11-02 14:05:36 +01:00
467 changed files with 18708 additions and 2131 deletions
@@ -208,7 +208,8 @@ class VoiceMessageAttachmentCacheManager {
return
}
let newURL = temporaryFilesFolderURL.appendingPathComponent(identifier).appendingPathExtension("m4a")
let fileExtension = filePath.hasSuffix(".mp4") ? "mp4" : "m4a"
let newURL = temporaryFilesFolderURL.appendingPathComponent(identifier).appendingPathExtension(fileExtension)
let conversionCompletion: (Result<Void, VoiceMessageAudioConverterError>) -> Void = { result in
self.workQueue.async {
@@ -42,7 +42,12 @@ struct VoiceMessageAudioConverter {
static func convertToMPEG4AAC(sourceURL: URL, destinationURL: URL, completion: @escaping (Result<Void, VoiceMessageAudioConverterError>) -> Void) {
DispatchQueue.global(qos: .userInitiated).async {
do {
try OGGConverter.convertOpusOGGToM4aFile(src: sourceURL, dest: destinationURL)
if sourceURL.pathExtension == "mp4" {
try FileManager.default.copyItem(atPath: sourceURL.path, toPath: destinationURL.path)
} else {
try OGGConverter.convertOpusOGGToM4aFile(src: sourceURL, dest: destinationURL)
}
DispatchQueue.main.async {
completion(.success(()))
}
@@ -35,12 +35,13 @@ enum VoiceMessageAudioPlayerError: Error {
class VoiceMessageAudioPlayer: NSObject {
private var playerItem: AVPlayerItem?
private var audioPlayer: AVPlayer?
private var audioPlayer: AVQueuePlayer?
private var statusObserver: NSKeyValueObservation?
private var playbackBufferEmptyObserver: NSKeyValueObservation?
private var rateObserver: NSKeyValueObservation?
private var playToEndObserver: NSObjectProtocol?
private var appBackgroundObserver: NSObjectProtocol?
private let delegateContainer = DelegateContainer()
@@ -63,6 +64,14 @@ class VoiceMessageAudioPlayer: NSObject {
return abs(CMTimeGetSeconds(audioPlayer?.currentTime() ?? .zero))
}
var playerItems: [AVPlayerItem] {
guard let audioPlayer = audioPlayer else {
return []
}
return audioPlayer.items()
}
private(set) var isStopped = true
deinit {
@@ -84,11 +93,30 @@ class VoiceMessageAudioPlayer: NSObject {
}
playerItem = AVPlayerItem(url: url)
audioPlayer = AVPlayer(playerItem: playerItem)
audioPlayer = AVQueuePlayer(playerItem: playerItem)
addObservers()
}
func addContentFromURL(_ url: URL) {
let playerItem = AVPlayerItem(url: url)
audioPlayer?.insert(playerItem, after: nil)
// audioPlayerDidFinishPlaying must be called on this last AVPlayerItem
NotificationCenter.default.removeObserver(playToEndObserver as Any)
playToEndObserver = NotificationCenter.default.addObserver(forName: Notification.Name.AVPlayerItemDidPlayToEndTime, object: playerItem, queue: nil) { [weak self] notification in
guard let self = self else { return }
self.delegateContainer.notifyDelegatesWithBlock { delegate in
(delegate as? VoiceMessageAudioPlayerDelegate)?.audioPlayerDidFinishPlaying(self)
}
}
}
func removeAllPlayerItems() {
audioPlayer?.removeAllItems()
}
func unloadContent() {
url = nil
audioPlayer?.replaceCurrentItem(with: nil)
@@ -121,7 +149,7 @@ class VoiceMessageAudioPlayer: NSObject {
audioPlayer?.seek(to: .zero)
}
func seekToTime(_ time: TimeInterval, completionHandler:@escaping (Bool) -> Void = { _ in }) {
func seekToTime(_ time: TimeInterval, completionHandler: @escaping (Bool) -> Void = { _ in }) {
audioPlayer?.seek(to: CMTime(seconds: time, preferredTimescale: 60000), completionHandler: completionHandler)
}
@@ -198,6 +226,15 @@ class VoiceMessageAudioPlayer: NSObject {
(delegate as? VoiceMessageAudioPlayerDelegate)?.audioPlayerDidFinishPlaying(self)
}
}
appBackgroundObserver = NotificationCenter.default.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: nil, queue: nil) { [weak self] _ in
guard let self = self, !BuildSettings.allowBackgroundAudioMessagePlayback else { return }
self.pause()
self.delegateContainer.notifyDelegatesWithBlock { delegate in
(delegate as? VoiceMessageAudioPlayerDelegate)?.audioPlayerDidPausePlaying(self)
}
}
}
private func removeObservers() {
@@ -205,6 +242,7 @@ class VoiceMessageAudioPlayer: NSObject {
playbackBufferEmptyObserver?.invalidate()
rateObserver?.invalidate()
NotificationCenter.default.removeObserver(playToEndObserver as Any)
NotificationCenter.default.removeObserver(appBackgroundObserver as Any)
}
}
@@ -183,6 +183,10 @@ import MediaPlayer
}
private func setUpRemoteCommandCenter() {
guard BuildSettings.allowBackgroundAudioMessagePlayback else {
return
}
displayLink.isPaused = false
UIApplication.shared.beginReceivingRemoteControlEvents()
@@ -252,14 +256,8 @@ import MediaPlayer
return
}
let artwork = MPMediaItemArtwork(boundsSize: Constants.roomAvatarImageSize) { [weak self] size in
return self?.roomAvatar ?? UIImage()
}
let nowPlayingInfoCenter = MPNowPlayingInfoCenter.default()
nowPlayingInfoCenter.nowPlayingInfo = [MPMediaItemPropertyTitle: audioPlayer.displayName ?? VectorL10n.voiceMessageLockScreenPlaceholder,
MPMediaItemPropertyArtist: currentRoomSummary?.displayname as Any,
MPMediaItemPropertyArtwork: artwork,
nowPlayingInfoCenter.nowPlayingInfo = [MPMediaItemPropertyTitle: VectorL10n.voiceMessageLockScreenPlaceholder,
MPMediaItemPropertyPlaybackDuration: audioPlayer.duration as Any,
MPNowPlayingInfoPropertyElapsedPlaybackTime: audioPlayer.currentTime as Any]
}
@@ -88,6 +88,8 @@ class VoiceMessageToolbarView: PassthroughView, NibLoadable, Themable, UIGesture
@IBOutlet private var toastNotificationContainerView: UIView!
@IBOutlet private var toastNotificationLabel: UILabel!
@IBOutlet var containersTopConstraints: [NSLayoutConstraint]!
private var playbackView: VoiceMessagePlaybackView!
private var cancelLabelToRecordButtonDistance: CGFloat = 0.0
@@ -1,16 +1,16 @@
<?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">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21225" 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"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21207"/>
<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"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="VoiceMessageToolbarView" customModule="Riot" customModuleProvider="target">
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="VoiceMessageToolbarView" customModule="Element" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="544" height="72"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
@@ -19,7 +19,7 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="XRB-CY-ijK" customClass="PassthroughView" customModule="Riot" customModuleProvider="target">
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="XRB-CY-ijK" customClass="PassthroughView" customModule="Element" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="544" height="72"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="8fP-9K-WTa">
@@ -71,7 +71,7 @@
<constraint firstAttribute="height" constant="152" id="li1-Bd-px2"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="dyu-ha-046" customClass="PassthroughView" customModule="Riot" customModuleProvider="target">
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="dyu-ha-046" customClass="PassthroughView" customModule="Element" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="544" height="72"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="6FH-4Q-Z5e">
@@ -267,12 +267,14 @@
<outlet property="slideToCancelLabel" destination="Ydw-Nb-zP6" id="l4Y-Eg-Qwc"/>
<outlet property="toastNotificationContainerView" destination="HDF-2Z-UHZ" id="8Ty-Gl-XnP"/>
<outlet property="toastNotificationLabel" destination="gZJ-ep-9Bz" id="soa-bs-C37"/>
<outletCollection property="containersTopConstraints" destination="XRb-zW-xdf" collectionClass="NSMutableArray" id="0JK-wT-gdZ"/>
<outletCollection property="containersTopConstraints" destination="tEJ-94-MLM" collectionClass="NSMutableArray" id="pY3-hI-Sda"/>
</connections>
<point key="canvasLocation" x="10.144927536231885" y="456.69642857142856"/>
</view>
</objects>
<resources>
<image name="chevron.left" catalog="system" width="96" height="128"/>
<image name="chevron.left" catalog="system" width="97" height="128"/>
<image name="room_context_menu_delete" width="24" height="24"/>
<image name="send_icon" width="36" height="36"/>
<image name="voice_message_cancel_gradient" width="104" height="47"/>