From 488814cf572b481f50945fe07f8e800c24d14407 Mon Sep 17 00:00:00 2001 From: Brandon T Date: Mon, 9 Dec 2024 18:40:07 -0500 Subject: [PATCH 1/4] Add button to QRCode Camera for URL Display --- .../RecentSearchQRCodeScannerController.swift | 114 +++++++++++++++++- 1 file changed, 110 insertions(+), 4 deletions(-) diff --git a/ios/brave-ios/Sources/Brave/Frontend/Browser/Search/RecentSearchQRCodeScannerController.swift b/ios/brave-ios/Sources/Brave/Frontend/Browser/Search/RecentSearchQRCodeScannerController.swift index 754ba1d49476..cd164b934ac4 100644 --- a/ios/brave-ios/Sources/Brave/Frontend/Browser/Search/RecentSearchQRCodeScannerController.swift +++ b/ios/brave-ios/Sources/Brave/Frontend/Browser/Search/RecentSearchQRCodeScannerController.swift @@ -4,6 +4,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. import AVFoundation +import BraveCore import BraveShared import Foundation import Shared @@ -12,9 +13,11 @@ import UIKit class RecentSearchQRCodeScannerController: UIViewController { private let scannerView = ScannerView() - private var didScan: Bool = false + private var didScan = false private var onDidScan: (_ string: String) -> Void + private var didProcessScanReusltsTask: Task? + public static var hasCameraSupport: Bool { if ProcessInfo.processInfo.isiOSAppOnVisionOS { // Apps on VisionOS can't access the main camera @@ -62,16 +65,36 @@ class RecentSearchQRCodeScannerController: UIViewController { // Feedback indicating code scan is finalized AudioServicesPlayAlertSound(SystemSoundID(kSystemSoundID_Vibrate)) UIImpactFeedbackGenerator(style: .medium).vibrate() - self.didScan = true - self.onDidScan(string) - self.dismiss(animated: true, completion: nil) + + self.scannerView.scannedText = string + self.scannerView.scannedDisplayButton.isHidden = false + self.scannerView.scannedDisplayButton.addAction( + UIAction(handler: { [weak self] _ in + guard let self = self else { return } + + self.didProcessScanReusltsTask?.cancel() + self.scannerView.scannedDisplayButton.isHidden = true + self.onDidScan(string) + self.dismiss(animated: true, completion: nil) + }), + for: .touchUpInside + ) + + // didProcessScanReusltsTask = Task.delayed(bySeconds: 3.seconds) { @MainActor [weak self] in + // try Task.checkCancellation() + // guard let self = self else { return } + // + // self.scannerView.scannedDisplayButton.isHidden = true + // self.onDidScan(string) + // } } } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) + didProcessScanReusltsTask?.cancel() scannerView.cameraView.stopRunning() } @@ -142,12 +165,88 @@ extension RecentSearchQRCodeScannerController { $0.textColor = .braveLabel } + var scannedText: String? { + didSet { + scannedDisplayButton.setNeedsUpdateConfiguration() + } + } + + private var scannedTextTruncationMode: NSLineBreakMode = .byTruncatingHead + + private var scannedDisplayButtonConfiguration: UIButton.Configuration { + var configuration = UIButton.Configuration.filled() + configuration.buttonSize = .small + configuration.titleLineBreakMode = .byTruncatingTail + configuration.baseForegroundColor = UIColor.white + configuration.baseBackgroundColor = UIColor(braveSystemName: .primitivePurple60) + configuration.cornerStyle = .capsule + return configuration + } + + private(set) lazy var scannedDisplayButton = UIButton( + configuration: scannedDisplayButtonConfiguration + ).then { + $0.configurationUpdateHandler = { [unowned self] button in + var configuration = scannedDisplayButtonConfiguration + let processedScannedResult = processScannedText() + let truncationMode = processedScannedResult.truncationMode + + if let scannedText = processedScannedResult.string { + let paragraphStyle = NSMutableParagraphStyle() + paragraphStyle.lineBreakMode = truncationMode + paragraphStyle.baseWritingDirection = .leftToRight + + let title = NSAttributedString( + string: scannedText, + attributes: [ + .font: UIFont.preferredFont(forTextStyle: .body), + .paragraphStyle: paragraphStyle, + ] + ) + + configuration.attributedTitle = AttributedString(title) + } + + button.configuration = configuration + } + $0.isHidden = true + } + + private func processScannedText() -> (string: String?, truncationMode: NSLineBreakMode) { + if let text = scannedText, let url = URIFixup.getURL(text) { + let isRenderedLeftToRight = url.isRenderedLeftToRight + let isMixedCharset = !url.isUnidirectional + + var isLTR = isRenderedLeftToRight && !isMixedCharset + if isMixedCharset { + isLTR = true + } + + let scannedText = URLFormatter.formatURL( + URLOrigin(url: url).url?.absoluteString ?? url.absoluteString, + formatTypes: [ + .omitDefaults, .trimAfterHost, .omitHTTPS, .omitTrivialSubdomains, + ], + unescapeOptions: .normal + ) + + let truncationMode: NSLineBreakMode = + !["http", "https"].contains(url.scheme ?? "") || !isLTR + ? .byTruncatingTail : .byTruncatingHead + + return (scannedText, truncationMode) + } + + return (scannedText, .byTruncatingTail) + } + override init(frame: CGRect) { super.init(frame: frame) backgroundColor = .secondaryBraveBackground addSubview(cameraView) + addSubview(scannedDisplayButton) addSubview(scrollView) scrollView.addSubview(stackView) stackView.addStackViewItems( @@ -164,6 +263,13 @@ extension RecentSearchQRCodeScannerController { $0.width.lessThanOrEqualTo(375) } + scannedDisplayButton.snp.makeConstraints { + $0.centerX.equalTo(cameraView) + $0.bottom.equalTo(cameraView).inset(10) + $0.leading.greaterThanOrEqualTo(cameraView).inset(40) + $0.trailing.lessThanOrEqualTo(cameraView).inset(40) + } + scrollView.snp.makeConstraints { $0.top.equalTo(cameraView.snp.bottom).offset(10) $0.leading.trailing.bottom.equalToSuperview() From 341399a92746819892075e85faaec2796864b790 Mon Sep 17 00:00:00 2001 From: Brandon T Date: Mon, 9 Dec 2024 19:46:02 -0500 Subject: [PATCH 2/4] Get rid of UIButton.Configuration and use regular setup, to handle truncation properly --- .../RecentSearchQRCodeScannerController.swift | 51 ++++++------------- 1 file changed, 16 insertions(+), 35 deletions(-) diff --git a/ios/brave-ios/Sources/Brave/Frontend/Browser/Search/RecentSearchQRCodeScannerController.swift b/ios/brave-ios/Sources/Brave/Frontend/Browser/Search/RecentSearchQRCodeScannerController.swift index cd164b934ac4..672e64da3b5f 100644 --- a/ios/brave-ios/Sources/Brave/Frontend/Browser/Search/RecentSearchQRCodeScannerController.swift +++ b/ios/brave-ios/Sources/Brave/Frontend/Browser/Search/RecentSearchQRCodeScannerController.swift @@ -167,27 +167,6 @@ extension RecentSearchQRCodeScannerController { var scannedText: String? { didSet { - scannedDisplayButton.setNeedsUpdateConfiguration() - } - } - - private var scannedTextTruncationMode: NSLineBreakMode = .byTruncatingHead - - private var scannedDisplayButtonConfiguration: UIButton.Configuration { - var configuration = UIButton.Configuration.filled() - configuration.buttonSize = .small - configuration.titleLineBreakMode = .byTruncatingTail - configuration.baseForegroundColor = UIColor.white - configuration.baseBackgroundColor = UIColor(braveSystemName: .primitivePurple60) - configuration.cornerStyle = .capsule - return configuration - } - - private(set) lazy var scannedDisplayButton = UIButton( - configuration: scannedDisplayButtonConfiguration - ).then { - $0.configurationUpdateHandler = { [unowned self] button in - var configuration = scannedDisplayButtonConfiguration let processedScannedResult = processScannedText() let truncationMode = processedScannedResult.truncationMode @@ -204,24 +183,28 @@ extension RecentSearchQRCodeScannerController { ] ) - configuration.attributedTitle = AttributedString(title) + scannedDisplayButton.titleLabel?.lineBreakMode = truncationMode + scannedDisplayButton.setAttributedTitle(title, for: .normal) + } else { + scannedDisplayButton.setTitle(nil, for: .normal) } - - button.configuration = configuration } + } + + private(set) lazy var scannedDisplayButton = UIButton().then { + $0.contentEdgeInsets = UIEdgeInsets(top: 12, left: 16, bottom: 12, right: 16) + $0.titleLabel?.font = UIFont.preferredFont(forTextStyle: .body) + $0.titleLabel?.lineBreakMode = .byTruncatingTail + $0.setTitleColor(UIColor.white, for: .normal) + $0.backgroundColor = UIColor(braveSystemName: .primitivePurple60) + $0.layer.cornerRadius = 10 + $0.layer.cornerCurve = .continuous + $0.layer.masksToBounds = true $0.isHidden = true } private func processScannedText() -> (string: String?, truncationMode: NSLineBreakMode) { if let text = scannedText, let url = URIFixup.getURL(text) { - let isRenderedLeftToRight = url.isRenderedLeftToRight - let isMixedCharset = !url.isUnidirectional - - var isLTR = isRenderedLeftToRight && !isMixedCharset - if isMixedCharset { - isLTR = true - } - let scannedText = URLFormatter.formatURL( URLOrigin(url: url).url?.absoluteString ?? url.absoluteString, formatTypes: [ @@ -231,9 +214,7 @@ extension RecentSearchQRCodeScannerController { ) let truncationMode: NSLineBreakMode = - !["http", "https"].contains(url.scheme ?? "") || !isLTR - ? .byTruncatingTail : .byTruncatingHead - + ["http", "https"].contains(url.scheme ?? "") ? .byTruncatingHead : .byTruncatingTail return (scannedText, truncationMode) } From e8486e294ff054887c5c0de57f414b367237e821 Mon Sep 17 00:00:00 2001 From: Brandon T Date: Tue, 14 Jan 2025 09:13:55 -0500 Subject: [PATCH 3/4] Remove unused code --- .../Search/RecentSearchQRCodeScannerController.swift | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/ios/brave-ios/Sources/Brave/Frontend/Browser/Search/RecentSearchQRCodeScannerController.swift b/ios/brave-ios/Sources/Brave/Frontend/Browser/Search/RecentSearchQRCodeScannerController.swift index 672e64da3b5f..1f727fd5e387 100644 --- a/ios/brave-ios/Sources/Brave/Frontend/Browser/Search/RecentSearchQRCodeScannerController.swift +++ b/ios/brave-ios/Sources/Brave/Frontend/Browser/Search/RecentSearchQRCodeScannerController.swift @@ -80,14 +80,6 @@ class RecentSearchQRCodeScannerController: UIViewController { }), for: .touchUpInside ) - - // didProcessScanReusltsTask = Task.delayed(bySeconds: 3.seconds) { @MainActor [weak self] in - // try Task.checkCancellation() - // guard let self = self else { return } - // - // self.scannerView.scannedDisplayButton.isHidden = true - // self.onDidScan(string) - // } } } @@ -195,7 +187,7 @@ extension RecentSearchQRCodeScannerController { $0.contentEdgeInsets = UIEdgeInsets(top: 12, left: 16, bottom: 12, right: 16) $0.titleLabel?.font = UIFont.preferredFont(forTextStyle: .body) $0.titleLabel?.lineBreakMode = .byTruncatingTail - $0.setTitleColor(UIColor.white, for: .normal) + $0.setTitleColor(.white, for: .normal) $0.backgroundColor = UIColor(braveSystemName: .primitivePurple60) $0.layer.cornerRadius = 10 $0.layer.cornerCurve = .continuous From a4d07c35dcd3dd9a5ae2eceab237c2ea9ee381fa Mon Sep 17 00:00:00 2001 From: Brandon T Date: Tue, 14 Jan 2025 14:41:38 -0500 Subject: [PATCH 4/4] Remove deleted task --- .../Browser/Search/RecentSearchQRCodeScannerController.swift | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ios/brave-ios/Sources/Brave/Frontend/Browser/Search/RecentSearchQRCodeScannerController.swift b/ios/brave-ios/Sources/Brave/Frontend/Browser/Search/RecentSearchQRCodeScannerController.swift index 1f727fd5e387..762394a6e54e 100644 --- a/ios/brave-ios/Sources/Brave/Frontend/Browser/Search/RecentSearchQRCodeScannerController.swift +++ b/ios/brave-ios/Sources/Brave/Frontend/Browser/Search/RecentSearchQRCodeScannerController.swift @@ -16,8 +16,6 @@ class RecentSearchQRCodeScannerController: UIViewController { private var didScan = false private var onDidScan: (_ string: String) -> Void - private var didProcessScanReusltsTask: Task? - public static var hasCameraSupport: Bool { if ProcessInfo.processInfo.isiOSAppOnVisionOS { // Apps on VisionOS can't access the main camera @@ -73,7 +71,6 @@ class RecentSearchQRCodeScannerController: UIViewController { UIAction(handler: { [weak self] _ in guard let self = self else { return } - self.didProcessScanReusltsTask?.cancel() self.scannerView.scannedDisplayButton.isHidden = true self.onDidScan(string) self.dismiss(animated: true, completion: nil) @@ -86,7 +83,6 @@ class RecentSearchQRCodeScannerController: UIViewController { override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) - didProcessScanReusltsTask?.cancel() scannerView.cameraView.stopRunning() }