mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-05-01 13:46:57 +02:00
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:
@@ -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"/>
|
||||
|
||||
Reference in New Issue
Block a user