From d03c4f299b58c19fcf6e9ecfd3a501fe070b24b1 Mon Sep 17 00:00:00 2001
From: TSI-amrutwaghmare <96108296+TSI-amrutwaghmare@users.noreply.github.com>
Date: Mon, 23 Oct 2023 16:49:46 +0530
Subject: [PATCH 1/4] NMC 2261 - Audio record and upload customisation
---
.../NextcloudUnitTests/AudioUploadTests.swift | 52 +++
.../NCAudioRecorderViewController.swift | 4 +-
.../Create cloud/FolderPathCustomCell.swift | 33 ++
.../Create cloud/FolderPathCustomCell.xib | 74 ++++
.../NCCreateFormUploadVoiceNote.storyboard | 112 ++++++
.../NCCreateFormUploadVoiceNote.swift | 359 ++++++++++++++++++
.../Main/Create cloud/TextTableViewCell.swift | 94 +++++
.../Main/Create cloud/TextTableViewCell.xib | 82 ++++
8 files changed, 808 insertions(+), 2 deletions(-)
create mode 100644 Tests/NextcloudUnitTests/AudioUploadTests.swift
create mode 100644 iOSClient/Main/Create cloud/FolderPathCustomCell.swift
create mode 100644 iOSClient/Main/Create cloud/FolderPathCustomCell.xib
create mode 100644 iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.storyboard
create mode 100644 iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.swift
create mode 100644 iOSClient/Main/Create cloud/TextTableViewCell.swift
create mode 100644 iOSClient/Main/Create cloud/TextTableViewCell.xib
diff --git a/Tests/NextcloudUnitTests/AudioUploadTests.swift b/Tests/NextcloudUnitTests/AudioUploadTests.swift
new file mode 100644
index 0000000000..2ea01a8085
--- /dev/null
+++ b/Tests/NextcloudUnitTests/AudioUploadTests.swift
@@ -0,0 +1,52 @@
+//
+// AudioUploadTests.swift
+// NextcloudTests
+//
+// Created by A200020526 on 13/06/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import XCTest
+@testable import Nextcloud
+
+final class AudioUploadTests: XCTestCase {
+ var viewController:NCAudioRecorderViewController?
+
+ override func setUpWithError() throws {
+ // Put setup code here. This method is called before the invocation of each test method in the class.
+ // Step 1. Create an instance of UIStoryboard
+ let viewController = UIStoryboard(name: "NCAudioRecorderViewController", bundle: nil).instantiateInitialViewController() as? NCAudioRecorderViewController
+ // Step 3. Make the viewDidLoad() execute.
+ viewController?.loadViewIfNeeded()
+ }
+
+ override func tearDownWithError() throws {
+ // Put teardown code here. This method is called after the invocation of each test method in the class.
+ viewController = nil
+ }
+
+ func testExample() throws {
+ // This is an example of a functional test case.
+ // Use XCTAssert and related functions to verify your tests produce the correct results.
+ // Any test you write for XCTest can be annotated as throws and async.
+ // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
+ // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
+ }
+
+ func testPerformanceExample() throws {
+ // This is an example of a performance test case.
+ self.measure {
+ // Put the code you want to measure the time of here.
+ }
+ }
+
+ func testAudioMeterUpdateAfterDb(){
+ viewController?.audioMeterDidUpdate(0.5)
+ XCTAssertNotNil(!(viewController?.durationLabel.text?.isEmpty ?? false))
+ }
+
+ func testStartRecorder(){
+ viewController?.startStop()
+ XCTAssertEqual(viewController?.recording.state, nil, "Test start audio recorder")
+ }
+}
diff --git a/iOSClient/AudioRecorder/NCAudioRecorderViewController.swift b/iOSClient/AudioRecorder/NCAudioRecorderViewController.swift
index ce6572903c..75f3e88bce 100644
--- a/iOSClient/AudioRecorder/NCAudioRecorderViewController.swift
+++ b/iOSClient/AudioRecorder/NCAudioRecorderViewController.swift
@@ -54,7 +54,7 @@ class NCAudioRecorderViewController: UIViewController, NCAudioRecorderDelegate {
view.backgroundColor = .clear
contentContainerView.backgroundColor = UIColor.lightGray
- voiceRecordHUD.fillColor = UIColor.green
+ voiceRecordHUD.fillColor = NCBrandColor.shared.progressColorGreen60
Task {
self.fileName = await NCNetworking.shared.createFileName(fileNameBase: NSLocalizedString("_untitled_", comment: "") + ".m4a", account: self.session.account, serverUrl: controller.currentServerUrl())
@@ -134,7 +134,7 @@ class NCAudioRecorderViewController: UIViewController, NCAudioRecorderDelegate {
}
voiceRecordHUD.update(CGFloat(rate))
- voiceRecordHUD.fillColor = UIColor.green
+ voiceRecordHUD.fillColor = NCBrandColor.shared.progressColorGreen60
let formatter = DateComponentsFormatter()
formatter.allowedUnits = [.second]
diff --git a/iOSClient/Main/Create cloud/FolderPathCustomCell.swift b/iOSClient/Main/Create cloud/FolderPathCustomCell.swift
new file mode 100644
index 0000000000..b7ba63f9ef
--- /dev/null
+++ b/iOSClient/Main/Create cloud/FolderPathCustomCell.swift
@@ -0,0 +1,33 @@
+//
+// FolderPathCustomCell.swift
+// Nextcloud
+//
+// Created by Sumit on 28/04/21.
+// Copyright © 2021 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+
+class FolderPathCustomCell: XLFormButtonCell{
+
+ @IBOutlet weak var photoLabel: UILabel!
+ @IBOutlet weak var folderImage: UIImageView!
+ @IBOutlet weak var bottomLineView: UIView!
+
+ override func awakeFromNib() {
+ super.awakeFromNib()
+ }
+
+ override func configure() {
+ super.configure()
+ }
+
+ override func update() {
+ super.update()
+ if (rowDescriptor.tag == "PhotoButtonDestinationFolder"){
+ bottomLineView.isHidden = true
+ }else{
+ bottomLineView.isHidden = false
+ }
+ }
+}
diff --git a/iOSClient/Main/Create cloud/FolderPathCustomCell.xib b/iOSClient/Main/Create cloud/FolderPathCustomCell.xib
new file mode 100644
index 0000000000..a231ae7c72
--- /dev/null
+++ b/iOSClient/Main/Create cloud/FolderPathCustomCell.xib
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.storyboard b/iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.storyboard
new file mode 100644
index 0000000000..1b5344ccee
--- /dev/null
+++ b/iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.storyboard
@@ -0,0 +1,112 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.swift b/iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.swift
new file mode 100644
index 0000000000..22354160c0
--- /dev/null
+++ b/iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.swift
@@ -0,0 +1,359 @@
+//
+// NCCreateFormUploadVoiceNote.swift
+// Nextcloud
+//
+// Created by Marino Faggiana on 9/03/2019.
+// Copyright © 2019 Marino Faggiana. All rights reserved.
+//
+// Author Marino Faggiana
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+
+import UIKit
+import NextcloudKit
+
+class NCCreateFormUploadVoiceNote: XLFormViewController, NCSelectDelegate, AVAudioPlayerDelegate, NCCreateFormUploadConflictDelegate {
+
+ @IBOutlet weak var buttonPlayStop: UIButton!
+ @IBOutlet weak var labelTimer: UILabel!
+ @IBOutlet weak var labelDuration: UILabel!
+ @IBOutlet weak var progressView: UIProgressView!
+
+ private let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
+ let utilityFileSystem = NCUtilityFileSystem()
+ let utility = NCUtility()
+ private var serverUrl = ""
+ private var titleServerUrl = ""
+ private var fileName = ""
+ private var fileNamePath = ""
+ private var durationPlayer: TimeInterval = 0
+ private var counterSecondPlayer: TimeInterval = 0
+
+ private var audioPlayer: AVAudioPlayer!
+ private var timer = Timer()
+
+ var cellBackgoundColor = UIColor.secondarySystemGroupedBackground
+
+ // MARK: - View Life Cycle
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_cancel_", comment: ""), style: UIBarButtonItem.Style.plain, target: self, action: #selector(cancel))
+ self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_save_", comment: ""), style: UIBarButtonItem.Style.plain, target: self, action: #selector(save))
+ self.navigationItem.leftBarButtonItem?.tintColor = NCBrandColor.shared.brand
+ self.navigationItem.rightBarButtonItem?.tintColor = NCBrandColor.shared.brand
+
+ self.tableView.separatorStyle = UITableViewCell.SeparatorStyle.none
+
+ view.backgroundColor = .systemGroupedBackground
+ tableView.backgroundColor = .systemGroupedBackground
+ cellBackgoundColor = .secondarySystemGroupedBackground
+
+ self.title = NSLocalizedString("_voice_memo_title_", comment: "")
+
+ // Button Play Stop
+ buttonPlayStop.setImage(UIImage(named: "audioPlay")!.image(color: NCBrandColor.shared.iconColor, size: 100), for: .normal)
+
+ // Progress view
+ progressView.progress = 0
+ progressView.progressTintColor = NCBrandColor.shared.customer
+ progressView.trackTintColor = .white
+ progressView.layer.borderWidth = 1
+ progressView.layer.cornerRadius = 5.0
+ progressView.layer.borderColor = NCBrandColor.shared.customer.cgColor
+
+ labelTimer.textColor = .label
+ labelDuration.textColor = .label
+
+ initializeForm()
+ }
+
+ override func viewWillAppear(_ animated: Bool) {
+ super.viewWillAppear(animated)
+
+ updateTimerUI()
+ }
+
+ override func viewDidDisappear(_ animated: Bool) {
+ super.viewDidDisappear(animated)
+
+ if audioPlayer.isPlaying {
+ stop()
+ }
+ }
+
+ public func setup(serverUrl: String, fileNamePath: String, fileName: String) {
+
+ if serverUrl == utilityFileSystem.getHomeServer(urlBase: appDelegate.urlBase, userId: appDelegate.userId) {
+ titleServerUrl = "/"
+ } else {
+ titleServerUrl = (serverUrl as NSString).lastPathComponent
+ }
+
+ self.fileName = fileName
+ self.serverUrl = serverUrl
+ self.fileNamePath = fileNamePath
+
+ // player
+ do {
+ try audioPlayer = AVAudioPlayer(contentsOf: URL(fileURLWithPath: fileNamePath))
+ audioPlayer.prepareToPlay()
+ audioPlayer.delegate = self
+ durationPlayer = TimeInterval(audioPlayer.duration)
+ } catch {
+ buttonPlayStop.isEnabled = false
+ }
+ }
+
+ // MARK: XLForm
+
+ func initializeForm() {
+
+ let form: XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+
+ var section: XLFormSectionDescriptor
+ var row: XLFormRowDescriptor
+
+ // Section: Destination Folder
+
+ section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("_save_path_", comment: "").uppercased())
+ section.footerTitle = ""
+ form.addFormSection(section)
+
+ XLFormViewController.cellClassesForRowDescriptorTypes()["kNMCFolderCustomCellType"] = FolderPathCustomCell.self
+
+ row = XLFormRowDescriptor(tag: "ButtonDestinationFolder", rowType: "kNMCFolderCustomCellType", title: self.titleServerUrl)
+ row.cellConfig["backgroundColor"] = UIColor.secondarySystemGroupedBackground
+ row.action.formSelector = #selector(changeDestinationFolder(_:))
+ row.cellConfig["folderImage.image"] = UIImage(named: "folder_nmcloud")?.image(color: NCBrandColor.shared.brandElement, size: 25)
+ row.cellConfig["photoLabel.textAlignment"] = NSTextAlignment.right.rawValue
+ row.cellConfig["photoLabel.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["photoLabel.textColor"] = UIColor.label //photos
+ if(self.titleServerUrl == "/"){
+ row.cellConfig["photoLabel.text"] = NSLocalizedString("_prefix_upload_path_", comment: "")
+ }else{
+ row.cellConfig["photoLabel.text"] = self.titleServerUrl
+ }
+ row.cellConfig["textLabel.text"] = ""
+ section.addFormRow(row)
+
+ // Section: File Name
+
+ XLFormViewController.cellClassesForRowDescriptorTypes()["kMyAppCustomCellType"] = TextTableViewCell.self
+
+ section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("_filename_", comment: "").uppercased())
+ form.addFormSection(section)
+
+ row = XLFormRowDescriptor(tag: "fileName", rowType: "kMyAppCustomCellType", title: NSLocalizedString("_filename_", comment: ""))
+ row.cellClass = TextTableViewCell.self
+ row.cellConfigAtConfigure["backgroundColor"] = UIColor.secondarySystemGroupedBackground;
+ row.cellConfig["fileNameTextField.textAlignment"] = NSTextAlignment.left.rawValue
+ row.cellConfig["fileNameTextField.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["fileNameTextField.textColor"] = UIColor.label
+ row.cellConfig["fileNameTextField.text"] = self.fileName
+ section.addFormRow(row)
+
+ self.form = form
+ }
+
+ override func formRowDescriptorValueHasChanged(_ formRow: XLFormRowDescriptor!, oldValue: Any!, newValue: Any!) {
+
+ super.formRowDescriptorValueHasChanged(formRow, oldValue: oldValue, newValue: newValue)
+
+ if formRow.tag == "fileName" {
+
+ self.form.delegate = nil
+
+ if let fileNameNew = formRow.value as? String {
+ self.fileName = utility.removeForbiddenCharacters(fileNameNew)
+ }
+
+
+ self.form.delegate = self
+ }
+ }
+
+ // MARK: TableViewDelegate
+
+ override func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
+ let header = view as? UITableViewHeaderFooterView
+ header?.textLabel?.font = UIFont.systemFont(ofSize: 13.0)
+ header?.textLabel?.textColor = .gray
+ header?.tintColor = cellBackgoundColor
+ }
+
+ // MARK: - Action
+
+ func dismissSelect(serverUrl: String?, metadata: tableMetadata?, type: String, items: [Any], indexPath: [IndexPath], overwrite: Bool, copy: Bool, move: Bool) {
+
+ if serverUrl != nil {
+
+ self.serverUrl = serverUrl!
+
+ if serverUrl == utilityFileSystem.getHomeServer(urlBase: appDelegate.urlBase, userId: appDelegate.userId) {
+ self.titleServerUrl = "/"
+ } else {
+ self.titleServerUrl = (serverUrl! as NSString).lastPathComponent
+ }
+
+ // Update
+ let row: XLFormRowDescriptor = self.form.formRow(withTag: "ButtonDestinationFolder")!
+ row.cellConfig["photoLabel.text"] = self.titleServerUrl
+ self.updateFormRow(row)
+ }
+ }
+
+ @objc func save() {
+
+ let rowFileName: XLFormRowDescriptor = self.form.formRow(withTag: "fileName")!
+ guard let name = (rowFileName.value as? String)?.trimmingCharacters(in: .whitespaces) else {
+ let alert = UIAlertController(title: "", message: NSLocalizedString("_prompt_insert_file_name", comment: ""), preferredStyle: .alert)
+ alert.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .cancel, handler: nil))
+ self.present(alert, animated: true)
+ return
+ }
+ let ext = (name as NSString).pathExtension.uppercased()
+ var fileNameSave = ""
+
+ if ext.isEmpty {
+ fileNameSave = name + ".m4a"
+ } else {
+ fileNameSave = (name as NSString).deletingPathExtension + ".m4a"
+ }
+
+ let metadataForUpload = NCManageDatabase.shared.createMetadata(account: self.appDelegate.account, user: self.appDelegate.user, userId: self.appDelegate.userId, fileName: fileNameSave, fileNameView: fileNameSave, ocId: UUID().uuidString, serverUrl: self.serverUrl, urlBase: self.appDelegate.urlBase, url: "", contentType: "")
+
+ metadataForUpload.session = NCNetworking.shared.sessionUploadBackground
+ metadataForUpload.sessionSelector = NCGlobal.shared.selectorUploadFile
+ metadataForUpload.status = NCGlobal.shared.metadataStatusWaitUpload
+ metadataForUpload.size = utilityFileSystem.getFileSize(filePath: fileNamePath)
+
+ if NCManageDatabase.shared.getMetadataConflict(account: appDelegate.account, serverUrl: serverUrl, fileNameView: fileNameSave) != nil {
+
+ guard let conflict = UIStoryboard(name: "NCCreateFormUploadConflict", bundle: nil).instantiateInitialViewController() as? NCCreateFormUploadConflict else { return }
+
+ conflict.textLabelDetailNewFile = NSLocalizedString("_now_", comment: "")
+ conflict.serverUrl = serverUrl
+ conflict.metadatasUploadInConflict = [metadataForUpload]
+ conflict.delegate = self
+
+ self.present(conflict, animated: true, completion: nil)
+
+ } else {
+
+ dismissAndUpload(metadataForUpload)
+ }
+ }
+
+ func dismissCreateFormUploadConflict(metadatas: [tableMetadata]?) {
+
+ if let metadatas, metadatas.count > 0 {
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
+ self.dismissAndUpload(metadatas[0])
+ }
+ }
+ }
+
+ func dismissAndUpload(_ metadata: tableMetadata) {
+
+ utilityFileSystem.copyFile(atPath: self.fileNamePath, toPath: utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView))
+
+ NCNetworkingProcessUpload.shared.createProcessUploads(metadatas: [metadata], completion: { _ in })
+
+ self.dismiss(animated: true, completion: nil)
+ }
+
+ @objc func cancel() {
+
+ try? FileManager.default.removeItem(atPath: fileNamePath)
+ self.dismiss(animated: true, completion: nil)
+ }
+
+ @objc func changeDestinationFolder(_ sender: XLFormRowDescriptor) {
+
+ self.deselectFormRow(sender)
+
+ let storyboard = UIStoryboard(name: "NCSelect", bundle: nil)
+ if let navigationController = storyboard.instantiateInitialViewController() as? UINavigationController,
+ let viewController = navigationController.topViewController as? NCSelect {
+
+ viewController.delegate = self
+ viewController.typeOfCommandView = .selectCreateFolder
+ viewController.includeDirectoryE2EEncryption = true
+
+ self.present(navigationController, animated: true, completion: nil)
+ }
+ }
+
+ // MARK: Player - Timer
+
+ func updateTimerUI() {
+ labelTimer.text = String().formatSecondsToString(counterSecondPlayer)
+ labelDuration.text = String().formatSecondsToString(durationPlayer)
+ progressView.progress = Float(counterSecondPlayer / durationPlayer)
+ }
+
+ @objc func updateTimer() {
+ counterSecondPlayer += 1
+ updateTimerUI()
+ }
+
+ @IBAction func playStop(_ sender: Any) {
+
+ if audioPlayer.isPlaying {
+
+ stop()
+
+ } else {
+
+ start()
+ }
+ }
+
+ func start() {
+
+ audioPlayer.prepareToPlay()
+ audioPlayer.play()
+
+ timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTimer), userInfo: nil, repeats: true)
+
+ buttonPlayStop.setImage(UIImage(named: "stop")!.image(color: UIColor.systemGray, size: 100), for: .normal)
+ }
+
+ func stop() {
+
+ audioPlayer.currentTime = 0.0
+ audioPlayer.stop()
+
+ timer.invalidate()
+ counterSecondPlayer = 0
+ progressView.progress = 0
+ updateTimerUI()
+
+ buttonPlayStop.setImage(UIImage(named: "audioPlay")!.image(color: UIColor.systemGray, size: 100), for: .normal)
+ }
+
+ func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
+
+ timer.invalidate()
+ counterSecondPlayer = 0
+ progressView.progress = 0
+ updateTimerUI()
+
+ buttonPlayStop.setImage(UIImage(named: "audioPlay")!.image(color: UIColor.systemGray, size: 100), for: .normal)
+ }
+}
diff --git a/iOSClient/Main/Create cloud/TextTableViewCell.swift b/iOSClient/Main/Create cloud/TextTableViewCell.swift
new file mode 100644
index 0000000000..11116a2ff5
--- /dev/null
+++ b/iOSClient/Main/Create cloud/TextTableViewCell.swift
@@ -0,0 +1,94 @@
+//
+// TextTableViewCell.swift
+// Nextcloud
+//
+// Created by Ashu on 23/04/21.
+// Copyright © 2021 Marino Faggiana. All rights reserved.
+//
+
+import UIKit
+
+class TextTableViewCell: XLFormBaseCell, UITextFieldDelegate {
+
+ @IBOutlet weak var fileNameTextField: UITextField!
+ @IBOutlet weak var topLineView: UIView!
+
+ override func awakeFromNib() {
+ super.awakeFromNib()
+ // Initialization code
+
+ fileNameTextField.delegate = self
+ topLineView.backgroundColor = UIColor.secondarySystemBackground
+
+ }
+
+ override func setSelected(_ selected: Bool, animated: Bool) {
+ super.setSelected(selected, animated: animated)
+
+ // Configure the view for the selected state
+ }
+
+ override func configure() {
+ super.configure()
+ }
+
+ override func update() {
+ super.update()
+ if (rowDescriptor.tag == "maskFileName"){
+ topLineView.isHidden = true
+ }else{
+ topLineView.isHidden = false
+ }
+
+ fileNameTextField.tintColor = UIColor.systemGray
+ fileNameTextField.selectedTextRange = fileNameTextField.textRange(from: fileNameTextField.beginningOfDocument, to: fileNameTextField.endOfDocument)
+ }
+
+ func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
+
+ if fileNameTextField == textField {
+ if let rowDescriptor = rowDescriptor, let text = self.fileNameTextField.text {
+
+ if (text + " ").isEmpty == false {
+ rowDescriptor.value = self.fileNameTextField.text! + string
+ } else {
+ rowDescriptor.value = nil
+ }
+ }
+ }
+
+ self.formViewController().textField(textField, shouldChangeCharactersIn: range, replacementString: string)
+
+ return true
+ }
+
+ func textFieldShouldReturn(_ textField: UITextField) -> Bool {
+ self.formViewController()?.textFieldShouldReturn(fileNameTextField)
+ return true
+ }
+
+ func textFieldShouldClear(_ textField: UITextField) -> Bool {
+ self.formViewController()?.textFieldShouldClear(fileNameTextField)
+ rowDescriptor.value = nil
+
+ self.formViewController().textField(textField, shouldChangeCharactersIn: NSRange.init().self, replacementString: "")
+ return true
+ }
+
+ override class func formDescriptorCellHeight(for rowDescriptor: XLFormRowDescriptor!) -> CGFloat {
+ return 45
+ }
+}
+
+extension UITextField {
+ @IBInspectable var placeholderColor: UIColor {
+ get {
+ return attributedPlaceholder?.attribute(.foregroundColor, at: 0, effectiveRange: nil) as? UIColor ?? .clear
+ }
+ set {
+ guard let attributedPlaceholder = attributedPlaceholder else { return }
+ let attributes: [NSAttributedString.Key: UIColor] = [.foregroundColor: newValue]
+ self.attributedPlaceholder = NSAttributedString(string: attributedPlaceholder.string, attributes: attributes)
+ }
+ }
+}
diff --git a/iOSClient/Main/Create cloud/TextTableViewCell.xib b/iOSClient/Main/Create cloud/TextTableViewCell.xib
new file mode 100644
index 0000000000..e4be37d230
--- /dev/null
+++ b/iOSClient/Main/Create cloud/TextTableViewCell.xib
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From 5f0a9a80acb161c7fae74dbc85f06e83bd1ac5c0 Mon Sep 17 00:00:00 2001
From: TSI-amrutwaghmare <96108296+TSI-amrutwaghmare@users.noreply.github.com>
Date: Wed, 10 Jan 2024 14:06:22 +0530
Subject: [PATCH 2/4] NMC 2261 - Empty filename alert issue
---
.../Main/Create cloud/NCCreateFormUploadVoiceNote.swift | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.swift b/iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.swift
index 22354160c0..f833e106dc 100644
--- a/iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.swift
+++ b/iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.swift
@@ -180,6 +180,8 @@ class NCCreateFormUploadVoiceNote: XLFormViewController, NCSelectDelegate, AVAud
if let fileNameNew = formRow.value as? String {
self.fileName = utility.removeForbiddenCharacters(fileNameNew)
+ } else {
+ self.fileName = ""
}
@@ -218,9 +220,8 @@ class NCCreateFormUploadVoiceNote: XLFormViewController, NCSelectDelegate, AVAud
}
@objc func save() {
-
- let rowFileName: XLFormRowDescriptor = self.form.formRow(withTag: "fileName")!
- guard let name = (rowFileName.value as? String)?.trimmingCharacters(in: .whitespaces) else {
+ let name = self.fileName
+ guard name.trimmingCharacters(in: .whitespaces) != "" else {
let alert = UIAlertController(title: "", message: NSLocalizedString("_prompt_insert_file_name", comment: ""), preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .cancel, handler: nil))
self.present(alert, animated: true)
From 3cf12f1411e9449a1a5dd4855230594adcc5ddd5 Mon Sep 17 00:00:00 2001
From: TSI-amrutwaghmare <96108296+TSI-amrutwaghmare@users.noreply.github.com>
Date: Wed, 10 Apr 2024 14:46:33 +0530
Subject: [PATCH 3/4] NMC 2261 - Save audio screen update after removed by
nextcloud version 5.0
---
.../NCAudioRecorderViewController.swift | 31 +++++++++++--------
.../NCCreateFormUploadVoiceNote.swift | 2 +-
2 files changed, 19 insertions(+), 14 deletions(-)
diff --git a/iOSClient/AudioRecorder/NCAudioRecorderViewController.swift b/iOSClient/AudioRecorder/NCAudioRecorderViewController.swift
index 75f3e88bce..81da1faee4 100644
--- a/iOSClient/AudioRecorder/NCAudioRecorderViewController.swift
+++ b/iOSClient/AudioRecorder/NCAudioRecorderViewController.swift
@@ -30,6 +30,7 @@ import QuartzCore
import NextcloudKit
class NCAudioRecorderViewController: UIViewController, NCAudioRecorderDelegate {
+
@IBOutlet weak var contentContainerView: UIView!
@IBOutlet weak var durationLabel: UILabel!
@IBOutlet weak var startStopLabel: UILabel!
@@ -42,6 +43,7 @@ class NCAudioRecorderViewController: UIViewController, NCAudioRecorderDelegate {
var session: NCSession.Session {
NCSession.shared.getSession(controller: controller)
}
+ let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
// MARK: - View Life Cycle
@@ -56,16 +58,14 @@ class NCAudioRecorderViewController: UIViewController, NCAudioRecorderDelegate {
contentContainerView.backgroundColor = UIColor.lightGray
voiceRecordHUD.fillColor = NCBrandColor.shared.progressColorGreen60
- Task {
- self.fileName = await NCNetworking.shared.createFileName(fileNameBase: NSLocalizedString("_untitled_", comment: "") + ".m4a", account: self.session.account, serverUrl: controller.currentServerUrl())
- recording = NCAudioRecorder(to: self.fileName)
- recording.delegate = self
- do {
- try self.recording.prepare()
- startStopLabel.text = NSLocalizedString("_voice_memo_start_", comment: "")
- } catch {
- print(error)
- }
+ self.fileName = NCUtilityFileSystem().createFileNameDate(NSLocalizedString("_voice_memo_filename_", comment: ""), ext: "m4a")
+ recording = NCAudioRecorder(to: self.fileName)
+ recording.delegate = self
+ do {
+ try self.recording.prepare()
+ startStopLabel.text = NSLocalizedString("_voice_memo_start_", comment: "")
+ } catch {
+ print(error)
}
}
@@ -83,8 +83,12 @@ class NCAudioRecorderViewController: UIViewController, NCAudioRecorderDelegate {
if recording.state == .record {
recording.stop()
voiceRecordHUD.update(0.0)
- dismiss(animated: true) {
- self.uploadMetadata()
+ dismiss(animated: true) { [self] in
+ guard let navigationController = UIStoryboard(name: "NCCreateFormUploadVoiceNote", bundle: nil).instantiateInitialViewController() as? UINavigationController,
+ let viewController = navigationController.topViewController as? NCCreateFormUploadVoiceNote else { return }
+ navigationController.modalPresentationStyle = .formSheet
+ viewController.setup(serverUrl: self.appDelegate.activeServerUrl, fileNamePath: NSTemporaryDirectory() + self.fileName, fileName: self.fileName)
+ self.appDelegate.window?.rootViewController?.present(navigationController, animated: true)
}
} else {
do {
@@ -148,7 +152,8 @@ class NCAudioRecorderViewController: UIViewController, NCAudioRecorderDelegate {
}
open class NCAudioRecorder: NSObject {
- public enum State: Int {
+
+ @objc public enum State: Int {
case none, record, play
}
diff --git a/iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.swift b/iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.swift
index f833e106dc..e4ea9c323c 100644
--- a/iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.swift
+++ b/iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.swift
@@ -273,7 +273,7 @@ class NCCreateFormUploadVoiceNote: XLFormViewController, NCSelectDelegate, AVAud
utilityFileSystem.copyFile(atPath: self.fileNamePath, toPath: utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView))
- NCNetworkingProcessUpload.shared.createProcessUploads(metadatas: [metadata], completion: { _ in })
+ NCNetworkingProcess.shared.createProcessUploads(metadatas: [metadata], completion: { _ in })
self.dismiss(animated: true, completion: nil)
}
From 871b1d0a743588158d8ebb23ccb5f70eef960967 Mon Sep 17 00:00:00 2001
From: harshada-15-tsys
Date: Wed, 9 Apr 2025 17:06:20 +0530
Subject: [PATCH 4/4] NMC 2261 - Audio recorder and upload customisation
---
Nextcloud.xcodeproj/project.pbxproj | 37 +++++++++++++++++++
.../NCAudioRecorderViewController.swift | 6 +--
2 files changed, 40 insertions(+), 3 deletions(-)
diff --git a/Nextcloud.xcodeproj/project.pbxproj b/Nextcloud.xcodeproj/project.pbxproj
index e214b2f751..d84b7b173c 100644
--- a/Nextcloud.xcodeproj/project.pbxproj
+++ b/Nextcloud.xcodeproj/project.pbxproj
@@ -92,6 +92,17 @@
AFCE353527E4ED5900FEA6C2 /* DateFormatter+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353427E4ED5900FEA6C2 /* DateFormatter+Extension.swift */; };
AFCE353727E4ED7B00FEA6C2 /* NCShareCells.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353627E4ED7B00FEA6C2 /* NCShareCells.swift */; };
AFCE353927E5DE0500FEA6C2 /* Shareable.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353827E5DE0400FEA6C2 /* Shareable.swift */; };
+ AFCE353927E5DE0500FEA6C2 /* NCShare+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353827E5DE0400FEA6C2 /* NCShare+Helper.swift */; };
+ B543154E2DA6913C00981E7E /* AudioUploadTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B543154D2DA6913C00981E7E /* AudioUploadTests.swift */; };
+ B54315552DA6929700981E7E /* NCCreateFormUploadVoiceNote.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54315522DA6929700981E7E /* NCCreateFormUploadVoiceNote.swift */; };
+ B543155A2DA6929700981E7E /* NCCreateFormUploadVoiceNote.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B54315512DA6929700981E7E /* NCCreateFormUploadVoiceNote.storyboard */; };
+ B543155B2DA6929700981E7E /* NCCreateFormUploadVoiceNote.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54315522DA6929700981E7E /* NCCreateFormUploadVoiceNote.swift */; };
+ B543155C2DA6929700981E7E /* FolderPathCustomCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B543154F2DA6929700981E7E /* FolderPathCustomCell.swift */; };
+ B543155D2DA6929700981E7E /* TextTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54315532DA6929700981E7E /* TextTableViewCell.swift */; };
+ B543155E2DA6929700981E7E /* TextTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B54315542DA6929700981E7E /* TextTableViewCell.xib */; };
+ B543155F2DA6929700981E7E /* FolderPathCustomCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B54315502DA6929700981E7E /* FolderPathCustomCell.xib */; };
+ B54315602DA6929700981E7E /* NCCreateFormUploadVoiceNote.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B54315512DA6929700981E7E /* NCCreateFormUploadVoiceNote.storyboard */; };
+ C04E2F232A17BB4D001BAD85 /* FilesIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C04E2F222A17BB4D001BAD85 /* FilesIntegrationTests.swift */; };
D575039F27146F93008DC9DC /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A0D1342591FBC5008F8A13 /* String+Extension.swift */; };
D5B6AA7827200C7200D49C24 /* NCActivityTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */; };
F310B1EF2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = F310B1EE2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift */; };
@@ -1329,6 +1340,14 @@
AFCE353427E4ED5900FEA6C2 /* DateFormatter+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateFormatter+Extension.swift"; sourceTree = ""; };
AFCE353627E4ED7B00FEA6C2 /* NCShareCells.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareCells.swift; sourceTree = ""; };
AFCE353827E5DE0400FEA6C2 /* Shareable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shareable.swift; sourceTree = ""; };
+ AFCE353827E5DE0400FEA6C2 /* NCShare+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCShare+Helper.swift"; sourceTree = ""; };
+ B543154D2DA6913C00981E7E /* AudioUploadTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioUploadTests.swift; sourceTree = ""; };
+ B543154F2DA6929700981E7E /* FolderPathCustomCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FolderPathCustomCell.swift; sourceTree = ""; };
+ B54315502DA6929700981E7E /* FolderPathCustomCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FolderPathCustomCell.xib; sourceTree = ""; };
+ B54315512DA6929700981E7E /* NCCreateFormUploadVoiceNote.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCCreateFormUploadVoiceNote.storyboard; sourceTree = ""; };
+ B54315522DA6929700981E7E /* NCCreateFormUploadVoiceNote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCCreateFormUploadVoiceNote.swift; sourceTree = ""; };
+ B54315532DA6929700981E7E /* TextTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextTableViewCell.swift; sourceTree = ""; };
+ B54315542DA6929700981E7E /* TextTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TextTableViewCell.xib; sourceTree = ""; };
C0046CDA2A17B98400D87C9D /* NextcloudUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NextcloudUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
C04E2F202A17BB4D001BAD85 /* NextcloudIntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NextcloudIntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCActivityTableViewCell.swift; sourceTree = ""; };
@@ -2117,6 +2136,8 @@
isa = PBXGroup;
children = (
AA52EB452D42AC5A0089C348 /* Placeholder.swift */,
+ B543154D2DA6913C00981E7E /* AudioUploadTests.swift */,
+ AF8ED1FB2757821000B8DBC4 /* NextcloudUnitTests.swift */,
);
path = NextcloudUnitTests;
sourceTree = "";
@@ -3194,6 +3215,12 @@
F7DFB7E9219C5A0500680748 /* Create cloud */ = {
isa = PBXGroup;
children = (
+ B543154F2DA6929700981E7E /* FolderPathCustomCell.swift */,
+ B54315502DA6929700981E7E /* FolderPathCustomCell.xib */,
+ B54315512DA6929700981E7E /* NCCreateFormUploadVoiceNote.storyboard */,
+ B54315522DA6929700981E7E /* NCCreateFormUploadVoiceNote.swift */,
+ B54315532DA6929700981E7E /* TextTableViewCell.swift */,
+ B54315542DA6929700981E7E /* TextTableViewCell.xib */,
F7FA7FFD2C0F4F3B0072FC60 /* Upload Assets */,
F7A509242C26BD5D00326106 /* NCCreateDocument.swift */,
F704B5E22430AA6F00632F5F /* NCCreateFormUploadConflict.storyboard */,
@@ -3913,6 +3940,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ B543155A2DA6929700981E7E /* NCCreateFormUploadVoiceNote.storyboard in Resources */,
F714803B262EBE3900693E51 /* MainInterface.storyboard in Resources */,
F7148054262ED51000693E51 /* NCListCell.xib in Resources */,
F7D57C8626317BDA00DE301D /* NCAccountRequest.storyboard in Resources */,
@@ -4006,6 +4034,9 @@
F70753F72542A9C000972D44 /* NCViewerMediaPage.storyboard in Resources */,
F7F4F10627ECDBDB008676F9 /* Inconsolata-Medium.ttf in Resources */,
F7AC934A296193050002BC0F /* Reasons to use Nextcloud.pdf in Resources */,
+ B543155E2DA6929700981E7E /* TextTableViewCell.xib in Resources */,
+ B543155F2DA6929700981E7E /* FolderPathCustomCell.xib in Resources */,
+ B54315602DA6929700981E7E /* NCCreateFormUploadVoiceNote.storyboard in Resources */,
F7A60F87292D215000FCE1F2 /* NCShareAccounts.storyboard in Resources */,
F761856A29E98543006EB3B0 /* NCIntro.storyboard in Resources */,
F719D9E0288D37A300762E33 /* NCColorPicker.storyboard in Resources */,
@@ -4142,6 +4173,7 @@
files = (
AA52EB472D42AC9E0089C348 /* Placeholder.swift in Sources */,
F372087D2BAB4C0F006B5430 /* TestConstants.swift in Sources */,
+ B543154E2DA6913C00981E7E /* AudioUploadTests.swift in Sources */,
F78E2D6C29AF02DB0024D4F3 /* Database.swift in Sources */,
F7817CFE29801A3500FFBC65 /* Data+Extension.swift in Sources */,
);
@@ -4350,6 +4382,8 @@
F33EE6F52BF4C9B200CA1A51 /* PKCS12.swift in Sources */,
F77DD6AB2C5CC093009448FB /* NCSession.swift in Sources */,
F76D364728A4F8BF00214537 /* NCActivityIndicator.swift in Sources */,
+ F73EF7CA2B0225610087E6E9 /* NCManageDatabase+PhotoLibrary.swift in Sources */,
+ B54315552DA6929700981E7E /* NCCreateFormUploadVoiceNote.swift in Sources */,
F7BFFD2A2C8854200029A201 /* NCHud.swift in Sources */,
F749B654297B0F2400087535 /* NCManageDatabase+Avatar.swift in Sources */,
AF22B208277B4E4C00DAB0CC /* NCCreateFormUploadConflictCell.swift in Sources */,
@@ -4696,6 +4730,9 @@
F3C587AE2D47E4FE004532DB /* PHAssetCollectionThumbnailLoader.swift in Sources */,
F78F74362163781100C2ADAD /* NCTrash.swift in Sources */,
F39298972A3B12CB00509762 /* BaseNCMoreCell.swift in Sources */,
+ B543155B2DA6929700981E7E /* NCCreateFormUploadVoiceNote.swift in Sources */,
+ B543155C2DA6929700981E7E /* FolderPathCustomCell.swift in Sources */,
+ B543155D2DA6929700981E7E /* TextTableViewCell.swift in Sources */,
AF2D7C7C2742556F00ADF566 /* NCShareLinkCell.swift in Sources */,
F7E41316294A19B300839300 /* UIView+Extension.swift in Sources */,
F7C30E00291BD2610017149B /* NCNetworkingE2EERename.swift in Sources */,
diff --git a/iOSClient/AudioRecorder/NCAudioRecorderViewController.swift b/iOSClient/AudioRecorder/NCAudioRecorderViewController.swift
index 81da1faee4..974168d979 100644
--- a/iOSClient/AudioRecorder/NCAudioRecorderViewController.swift
+++ b/iOSClient/AudioRecorder/NCAudioRecorderViewController.swift
@@ -87,8 +87,8 @@ class NCAudioRecorderViewController: UIViewController, NCAudioRecorderDelegate {
guard let navigationController = UIStoryboard(name: "NCCreateFormUploadVoiceNote", bundle: nil).instantiateInitialViewController() as? UINavigationController,
let viewController = navigationController.topViewController as? NCCreateFormUploadVoiceNote else { return }
navigationController.modalPresentationStyle = .formSheet
- viewController.setup(serverUrl: self.appDelegate.activeServerUrl, fileNamePath: NSTemporaryDirectory() + self.fileName, fileName: self.fileName)
- self.appDelegate.window?.rootViewController?.present(navigationController, animated: true)
+ viewController.setup(serverUrl: controller.currentServerUrl(), fileNamePath: NSTemporaryDirectory() + self.fileName, fileName: self.fileName)
+ UIApplication.shared.firstWindow?.rootViewController?.present(navigationController, animated: true)
}
} else {
do {
@@ -110,7 +110,7 @@ class NCAudioRecorderViewController: UIViewController, NCAudioRecorderDelegate {
url: "",
contentType: "",
session: self.session,
- sceneIdentifier: self.controller?.sceneIdentifier)
+ sceneIdentifier: self.appDelegate.sceneIdentifier)
metadata.session = NCNetworking.shared.sessionUploadBackground
metadata.sessionSelector = NCGlobal.shared.selectorUploadFile