From 8e4f63368693a8a65a583bed46f0ff97dbfad95f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20B=C3=B6ttger?= Date: Sat, 9 Aug 2025 19:26:43 +0200 Subject: [PATCH] Auto-start transmission when scanning QR code (merge) --- NearbyShare/Connection/NearbyConnection.swift | 2 + .../Connection/NearbyConnectionManager.swift | 99 +++++++++++++++--- .../Connection/OutboundNearbyConnection.swift | 11 +- NearbyShare/Model/DataModel.swift | 70 ++++++++++--- NearbyShare/Util/Helpers.swift | 21 +++- QuickDrop.xcodeproj/project.pbxproj | 32 ++++++ .../xcshareddata/swiftpm/Package.resolved | 29 ++++- QuickDrop/AppDelegate.swift | 7 +- .../QRBackground.imageset/Contents.json | 21 ++++ .../QRBackground.imageset/QRBackground.png | Bin 0 -> 67475 bytes ShareExtension/ShareViewController.swift | 37 ++++++- ShareExtension/View/SmallSheetView.swift | 42 ++++++-- 12 files changed, 328 insertions(+), 43 deletions(-) create mode 100644 QuickDrop/Assets.xcassets/QRBackground.imageset/Contents.json create mode 100644 QuickDrop/Assets.xcassets/QRBackground.imageset/QRBackground.png diff --git a/NearbyShare/Connection/NearbyConnection.swift b/NearbyShare/Connection/NearbyConnection.swift index 70f899d..b7f5fcc 100644 --- a/NearbyShare/Connection/NearbyConnection.swift +++ b/NearbyShare/Connection/NearbyConnection.swift @@ -57,6 +57,7 @@ class NearbyConnection { private var clientSeq: Int32 = 0 private(set) var pinCode: String? + private(set) var authKey: SymmetricKey? init(connection: NWConnection, id: String) { @@ -471,6 +472,7 @@ class NearbyConnection { let authString = NearbyConnection.hkdf(inputKeyMaterial: SymmetricKey(data: derivedSecretKey), salt: "UKEY2 v1 auth".data(using: .utf8)!, info: ukeyInfo, outputByteCount: 32) let nextSecret = NearbyConnection.hkdf(inputKeyMaterial: SymmetricKey(data: derivedSecretKey), salt: "UKEY2 v1 next".data(using: .utf8)!, info: ukeyInfo, outputByteCount: 32) + authKey = authString pinCode = NearbyConnection.pinCodeFromAuthKey(authString) let salt = Data([0x82, 0xAA, 0x55, 0xA0, 0xD3, 0x97, 0xF8, 0x83, 0x46, 0xCA, 0x1C, diff --git a/NearbyShare/Connection/NearbyConnectionManager.swift b/NearbyShare/Connection/NearbyConnectionManager.swift index 2ebfe39..84f927b 100644 --- a/NearbyShare/Connection/NearbyConnectionManager.swift +++ b/NearbyShare/Connection/NearbyConnectionManager.swift @@ -5,14 +5,16 @@ // Created by Grishka on 08.04.2023. // -import Foundation -import Network -import System - #if os(iOS) import UIKit #endif +import Foundation +import Network +import System +import SwiftECC +import CryptoKit + public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearbyConnectionDelegate, OutboundNearbyConnectionDelegate { private let sleepManager = SleepManager.shared @@ -25,6 +27,11 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb private var startedDeviceDiscovery = false private var browsers: [NWBrowser] = [] private let serviceTypes = ["_FC9F5ED42C8A._tcp."] + private var qrCodePublicKey: ECPublicKey? + private var qrCodePrivateKey: ECPrivateKey? + private var qrCodeAdvertisingToken: Data? + private var qrCodeNameEncryptionKey: SymmetricKey? + private var qrCodeData: Data? public let endpointID: [UInt8] = generateEndpointID() public var mainAppDelegate: (any MainAppDelegate)? @@ -109,7 +116,7 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb func getEndpointInfo() -> EndpointInfo { #if os(macOS) - let endpointInfo = EndpointInfo(name: Host.current().localizedName!, deviceType: .computer) + let endpointInfo = EndpointInfo(name: Host.current().localizedName ?? "Mac", deviceType: .computer) #else let endpointInfo = EndpointInfo(name: UIDevice.current.name, deviceType: .phone) #endif @@ -230,22 +237,56 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb } guard let endpointID = endpointID(for: service) else { return } var foundService = FoundServiceInfo(service: service) - + guard case let NWBrowser.Result.Metadata.bonjour(txtRecord) = service.metadata else { return } guard let endpointInfoEncoded = txtRecord.dictionary["n"] else { return } - guard let endpointInfo = Data.dataFromUrlSafeBase64(endpointInfoEncoded) else { return } - guard endpointInfo.count >= 19 else { return } - let deviceType = RemoteDeviceInfo.DeviceType.fromRawValue(value: (Int(endpointInfo[0]) >> 1) & 7) - let deviceNameLength = Int(endpointInfo[17]) - guard endpointInfo.count >= deviceNameLength + 17 else { return } - guard let deviceName = String(data: endpointInfo.subdata(in: 18 ..< (18 + deviceNameLength)), encoding: .utf8) else { return } - - let deviceInfo = RemoteDeviceInfo(name: deviceName, type: deviceType, id: endpointID) + guard let endpointInfoData = Data.dataFromUrlSafeBase64(endpointInfoEncoded) else { return } + guard var endpointInfo = EndpointInfo(data: endpointInfoData) else { return } + var deviceInfo: RemoteDeviceInfo? + + if let _ = endpointInfo.name { + deviceInfo = addFoundDevice(foundService: &foundService, endpointInfo: endpointInfo, endpointID: endpointID) + } + + if let qrData = endpointInfo.qrCodeData, let qrCodeAdvertisingToken = qrCodeAdvertisingToken, let qrCodeNameEncryptionKey = qrCodeNameEncryptionKey { + + log("[NearbyConnectionManager] Device has QR data: \(qrData.base64EncodedString()), advertising token is \(qrCodeAdvertisingToken.base64EncodedString())") + + if qrData == qrCodeAdvertisingToken { + if let deviceInfo = deviceInfo { + for delegate in shareExtensionDelegates { + delegate.startTransferWithQrCode(device: deviceInfo) + } + } + } + else if qrData.count > 28 { + do { + let box = try AES.GCM.SealedBox(combined: qrData) + let decryptedName = try AES.GCM.open(box, using: qrCodeNameEncryptionKey, authenticating: qrCodeAdvertisingToken) + guard let name = String.init(data: decryptedName, encoding: .utf8) else { return } + endpointInfo.name = name + let deviceInfo = addFoundDevice(foundService: &foundService, endpointInfo: endpointInfo, endpointID: endpointID) + for delegate in shareExtensionDelegates { + delegate.startTransferWithQrCode(device: deviceInfo) + } + } catch { + log("[NearbyConnectionManager] Error decrypting QR code data of an invisible device: \(error)") + } + } + } + } + + + private func addFoundDevice(foundService: inout FoundServiceInfo, endpointInfo: EndpointInfo, endpointID: String) -> RemoteDeviceInfo { + let deviceInfo = RemoteDeviceInfo(info: endpointInfo, id: endpointID) foundService.device = deviceInfo foundServices[endpointID] = foundService + for delegate in shareExtensionDelegates { delegate.addDevice(device: deviceInfo) } + + return deviceInfo } @@ -256,6 +297,35 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb delegate.removeDevice(id: endpointID) } } + + + public func generateQrCodeKey() -> String{ + let domain = Domain.instance(curve: .EC256r1) + let (pubKey, privKey) = domain.makeKeyPair() + qrCodePublicKey = pubKey + qrCodePrivateKey = privKey + var keyData = Data() + keyData.append(contentsOf: [0, 0, 2]) + let keyBytes = Data(pubKey.w.x.asSignedBytes()) + // Sometimes, for some keys, there will be a leading zero byte. Strip that, Android really hates it (it breaks the endpoint info) + keyData.append(keyBytes.suffixOfAtMost(numBytes: 32)) + + let ikm = SymmetricKey(data: keyData) + qrCodeAdvertisingToken = NearbyConnection.hkdf(inputKeyMaterial: ikm, salt: Data(), info: "advertisingContext".data(using: .utf8)!, outputByteCount: 16).data() + qrCodeNameEncryptionKey = NearbyConnection.hkdf(inputKeyMaterial: ikm, salt: Data(), info: "encryptionKey".data(using: .utf8)!, outputByteCount: 16) + qrCodeData = keyData + + return keyData.urlSafeBase64EncodedString() + } + + + public func clearQrCodeKey(){ + qrCodePublicKey = nil + qrCodePrivateKey = nil + qrCodeAdvertisingToken = nil + qrCodeNameEncryptionKey = nil + qrCodeData = nil + } public func startOutgoingTransfer(deviceID: String, delegate: ShareExtensionDelegate, urls: [URL], textToSend: String?) { @@ -265,6 +335,7 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb let nwconn = NWConnection(to: info.service.endpoint, using: NWParameters(tls: .none, tcp: tcp)) let conn = OutboundNearbyConnection(connection: nwconn, id: deviceID, urlsToSend: urls, textToSend: textToSend) conn.delegate = self + conn.qrCodePrivateKey = qrCodePrivateKey let transfer = OutgoingTransferInfo(service: info.service, device: info.device!, connection: conn, delegate: delegate) outgoingTransfers[deviceID] = transfer conn.start() diff --git a/NearbyShare/Connection/OutboundNearbyConnection.swift b/NearbyShare/Connection/OutboundNearbyConnection.swift index d6c1dd8..039f752 100644 --- a/NearbyShare/Connection/OutboundNearbyConnection.swift +++ b/NearbyShare/Connection/OutboundNearbyConnection.swift @@ -26,6 +26,7 @@ class OutboundNearbyConnection: NearbyConnection { private var totalBytesSent: Int64 = 0 private var textPayloadID: Int64 = 0 + public var qrCodePrivateKey: ECPrivateKey? public var delegate: OutboundNearbyConnectionDelegate? enum State { @@ -174,7 +175,7 @@ class OutboundNearbyConnection: NearbyConnection { frame.v1.type = .connectionRequest frame.v1.connectionRequest = Location_Nearby_Connections_ConnectionRequestFrame() frame.v1.connectionRequest.endpointID = String(bytes: NearbyConnectionManager.shared.endpointID, encoding: .ascii)! - frame.v1.connectionRequest.endpointName = endpointInfo.name + frame.v1.connectionRequest.endpointName = endpointInfo.name ?? "QuickDrop" frame.v1.connectionRequest.endpointInfo = endpointInfo.serialize() frame.v1.connectionRequest.mediums = [.wifiLan] try sendFrameAsync(frame.serializedData()) @@ -277,6 +278,14 @@ class OutboundNearbyConnection: NearbyConnection { pairedEncryption.v1.pairedKeyEncryption = Sharing_Nearby_PairedKeyEncryptionFrame() pairedEncryption.v1.pairedKeyEncryption.secretIDHash = Data.randomData(length: 6) pairedEncryption.v1.pairedKeyEncryption.signedData = Data.randomData(length: 72) + + if let qrKey = qrCodePrivateKey, let authKey = authKey { + let signature = qrKey.sign(msg: authKey.data()) + var serializedSignature = Data(signature.r) + serializedSignature.append(Data(signature.s)) + pairedEncryption.v1.pairedKeyEncryption.qrCodeHandshakeData = serializedSignature + } + try sendTransferSetupFrame(pairedEncryption) currentState = .sentPairedKeyEncryption diff --git a/NearbyShare/Model/DataModel.swift b/NearbyShare/Model/DataModel.swift index 4586d21..ec84d14 100644 --- a/NearbyShare/Model/DataModel.swift +++ b/NearbyShare/Model/DataModel.swift @@ -7,6 +7,8 @@ import Foundation import Network +import CryptoKit +import SwiftECC protocol InboundNearbyConnectionDelegate { @@ -36,6 +38,7 @@ public protocol MainAppDelegate { public protocol ShareExtensionDelegate: AnyObject { func addDevice(device: RemoteDeviceInfo) func removeDevice(id: String) + func startTransferWithQrCode(device: RemoteDeviceInfo) func connectionWasEstablished(pinCode: String) func connectionFailed(with error: Error) func transferAccepted() @@ -45,19 +48,23 @@ public protocol ShareExtensionDelegate: AnyObject { public struct RemoteDeviceInfo: Identifiable, Equatable { - public let name: String + public let name: String? public let type: DeviceType + public let qrCodeData:Data? public var id: String? init(name: String, type: DeviceType, id: String? = nil) { self.name = name self.type = type self.id = id + self.qrCodeData = nil } - init(info: EndpointInfo) { - name = info.name - type = info.deviceType + init(info: EndpointInfo, id: String? = nil) { + self.name = info.name + self.type = info.deviceType + self.qrCodeData = info.qrCodeData + self.id = id } @@ -116,24 +123,63 @@ public struct FileMetadata { struct EndpointInfo { - let name: String + var name: String? let deviceType: RemoteDeviceInfo.DeviceType + let qrCodeData: Data? init(name: String, deviceType: RemoteDeviceInfo.DeviceType) { self.name = name self.deviceType = deviceType + self.qrCodeData = nil } init?(data: Data) { guard data.count > 17 else { return nil } - let deviceNameLength = Int(data[17]) - guard data.count >= deviceNameLength + 18 else { return nil } - guard let deviceName = String(data: data[18 ..< (18 + deviceNameLength)], encoding: .utf8) else { return nil } - let rawDeviceType = Int(data[0] & 7) >> 1 - name = deviceName - deviceType = RemoteDeviceInfo.DeviceType.fromRawValue(value: rawDeviceType) + + let hasName = (data[0] & 0x10) == 0 + let deviceNameLength: Int + let deviceName: String? + + if hasName { + deviceNameLength = Int(data[17]) + guard data.count >= deviceNameLength + 18 else { return nil } + guard let newDeviceName = String(data: data[18..<(18 + deviceNameLength)], encoding: .utf8) else { return nil } + deviceName = newDeviceName + } + else { + deviceNameLength = 0 + deviceName = nil + } + + let rawDeviceType: Int = Int(data[0] & 7) >> 1 + self.name = deviceName + self.deviceType = RemoteDeviceInfo.DeviceType.fromRawValue(value: rawDeviceType) + var offset = 1 + 16 + + if hasName { + offset = offset + 1 + deviceNameLength + } + + var qrCodeData: Data? = nil + + // read TLV records, if any + while data.count - offset > 2 { + let type = data[offset] + let length = Int(data[offset+1]) + offset = offset + 2 + if data.count - offset >= length{ + // QR code data + if type == 1 { + qrCodeData = data.subdata(in: offset.. 255 { nameChars = [UInt8](nameChars[0 ..< 255]) } diff --git a/NearbyShare/Util/Helpers.swift b/NearbyShare/Util/Helpers.swift index 4ac8d26..8d115e8 100644 --- a/NearbyShare/Util/Helpers.swift +++ b/NearbyShare/Util/Helpers.swift @@ -6,6 +6,7 @@ // import Foundation +import CryptoKit extension Data { func urlSafeBase64EncodedString() -> String { @@ -20,7 +21,7 @@ extension Data { }) } - + static func randomData(length: Int) -> Data { var data = Data(count: length) data.withUnsafeMutableBytes { @@ -29,7 +30,16 @@ extension Data { return data } - + + func suffixOfAtMost(numBytes: Int) -> Data{ + if count <= numBytes { + return self + } + + return subdata(in: count-numBytes.. Data? { var regularB64 = String(str.map { if $0 == "_" { @@ -53,6 +63,13 @@ extension Data { } +extension SymmetricKey{ + func data() -> Data{ + return withUnsafeBytes({return Data(bytes: $0.baseAddress!, count: $0.count)}) + } +} + + extension String { var dataFromHex: Data { var data = Data(capacity: count / 2) diff --git a/QuickDrop.xcodeproj/project.pbxproj b/QuickDrop.xcodeproj/project.pbxproj index fabd30c..533431a 100644 --- a/QuickDrop.xcodeproj/project.pbxproj +++ b/QuickDrop.xcodeproj/project.pbxproj @@ -95,6 +95,9 @@ EB9925802E45D53E00357055 /* sharing_enums.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB99257F2E45D53E00357055 /* sharing_enums.pb.swift */; }; EB9925812E45D53E00357055 /* sharing_enums.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB99257F2E45D53E00357055 /* sharing_enums.pb.swift */; }; EB9925822E45D53E00357055 /* sharing_enums.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB99257F2E45D53E00357055 /* sharing_enums.pb.swift */; }; + EB9925852E45EF0900357055 /* QRCode in Frameworks */ = {isa = PBXBuildFile; productRef = EB9925842E45EF0900357055 /* QRCode */; }; + EB9925872E45F12F00357055 /* QRCode in Frameworks */ = {isa = PBXBuildFile; productRef = EB9925862E45F12F00357055 /* QRCode */; }; + EB9925892E45F13500357055 /* QRCode in Frameworks */ = {isa = PBXBuildFile; productRef = EB9925882E45F13500357055 /* QRCode */; }; EBC05C5B2E33937200EECC18 /* ErrorAlertHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBC05C5A2E33936C00EECC18 /* ErrorAlertHandler.swift */; }; EBC05C5C2E3394EF00EECC18 /* ErrorAlertHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBC05C5A2E33936C00EECC18 /* ErrorAlertHandler.swift */; }; EBC05C712E33B57D00EECC18 /* Converter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBC05C6D2E33B57D00EECC18 /* Converter.swift */; }; @@ -243,6 +246,7 @@ files = ( EB5C37D82E377ACF0052EFD7 /* SwiftECC in Frameworks */, EB5C37D62E377ACF0052EFD7 /* SwiftProtobuf in Frameworks */, + EB9925852E45EF0900357055 /* QRCode in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -250,6 +254,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + EB9925872E45F12F00357055 /* QRCode in Frameworks */, EB2F9BFE2DA05F48004010A2 /* BezelNotification in Frameworks */, EB5C37DC2E377AD60052EFD7 /* SwiftECC in Frameworks */, EB5C37DA2E377AD60052EFD7 /* SwiftProtobuf in Frameworks */, @@ -262,6 +267,7 @@ files = ( EB5C37F02E377C3F0052EFD7 /* SwiftECC in Frameworks */, EB5C37EE2E377C3F0052EFD7 /* SwiftProtobuf in Frameworks */, + EB9925892E45F13500357055 /* QRCode in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -509,6 +515,7 @@ EB2F9BFD2DA05F48004010A2 /* BezelNotification */, EB5C37D92E377AD60052EFD7 /* SwiftProtobuf */, EB5C37DB2E377AD60052EFD7 /* SwiftECC */, + EB9925862E45F12F00357055 /* QRCode */, ); productName = NearDrop; productReference = 69DA9A0E29E0BF5100A442DA /* QuickDrop.app */; @@ -533,6 +540,7 @@ packageProductDependencies = ( EB5C37ED2E377C3F0052EFD7 /* SwiftProtobuf */, EB5C37EF2E377C3F0052EFD7 /* SwiftECC */, + EB9925882E45F13500357055 /* QRCode */, ); productName = QuickDropiOS; productReference = EB5C37E22E377C2E0052EFD7 /* QuickDropiOS.app */; @@ -589,6 +597,7 @@ 69DA9A2429E189EF00A442DA /* XCRemoteSwiftPackageReference "swift-protobuf" */, 69DA9A3429E1994C00A442DA /* XCRemoteSwiftPackageReference "SwiftECC" */, EB2F9BFC2DA05F48004010A2 /* XCRemoteSwiftPackageReference "BezelNotification" */, + EB9925832E45EF0900357055 /* XCRemoteSwiftPackageReference "qrcode" */, ); productRefGroup = 69DA9A0F29E0BF5100A442DA /* Products */; projectDirPath = ""; @@ -1183,6 +1192,14 @@ minimumVersion = 2.1.0; }; }; + EB9925832E45EF0900357055 /* XCRemoteSwiftPackageReference "qrcode" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/dagronf/qrcode.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 26.10.0; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -1221,6 +1238,21 @@ package = 69DA9A3429E1994C00A442DA /* XCRemoteSwiftPackageReference "SwiftECC" */; productName = SwiftECC; }; + EB9925842E45EF0900357055 /* QRCode */ = { + isa = XCSwiftPackageProductDependency; + package = EB9925832E45EF0900357055 /* XCRemoteSwiftPackageReference "qrcode" */; + productName = QRCode; + }; + EB9925862E45F12F00357055 /* QRCode */ = { + isa = XCSwiftPackageProductDependency; + package = EB9925832E45EF0900357055 /* XCRemoteSwiftPackageReference "qrcode" */; + productName = QRCode; + }; + EB9925882E45F13500357055 /* QRCode */ = { + isa = XCSwiftPackageProductDependency; + package = EB9925832E45EF0900357055 /* XCRemoteSwiftPackageReference "qrcode" */; + productName = QRCode; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 69DA9A0629E0BF5100A442DA /* Project object */; diff --git a/QuickDrop.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/QuickDrop.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 364f54f..da47079 100644 --- a/QuickDrop.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/QuickDrop.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "c998f2b1c34ecbb24a88c5711f17c1a351f48f3505ccd4cab4552e5760bd39a3", + "originHash" : "10744462d42c114b38e0002c6a341b37abcba576ba3203c8d951905fbbdf1f58", "pins" : [ { "identity" : "asn1", @@ -28,6 +28,15 @@ "version" : "1.21.0" } }, + { + "identity" : "qrcode", + "kind" : "remoteSourceControl", + "location" : "https://github.com/dagronf/qrcode.git", + "state" : { + "revision" : "177d4091f7fcce1efc22de2a5fcdb671bd103101", + "version" : "26.10.0" + } + }, { "identity" : "swift-cross-kit-types", "kind" : "remoteSourceControl", @@ -55,6 +64,15 @@ "version" : "1.30.0" } }, + { + "identity" : "swift-qrcode-generator", + "kind" : "remoteSourceControl", + "location" : "https://github.com/dagronf/swift-qrcode-generator", + "state" : { + "revision" : "2b1980b825f08a81ccc762b0c4d17fcde9d5e953", + "version" : "2.0.2" + } + }, { "identity" : "swiftecc", "kind" : "remoteSourceControl", @@ -63,6 +81,15 @@ "revision" : "2b672501e3a9361316898aa2d5b0e684c992e01e", "version" : "3.9.0" } + }, + { + "identity" : "swiftimagereadwrite", + "kind" : "remoteSourceControl", + "location" : "https://github.com/dagronf/SwiftImageReadWrite", + "state" : { + "revision" : "42ace2412279f18bc264bc306e96b51c36e12a33", + "version" : "1.9.2" + } } ], "version" : 3 diff --git a/QuickDrop/AppDelegate.swift b/QuickDrop/AppDelegate.swift index 1ad51a1..debe4e6 100644 --- a/QuickDrop/AppDelegate.swift +++ b/QuickDrop/AppDelegate.swift @@ -355,16 +355,17 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele let mainMessage: String + let name = device.name ?? "Android" switch transfer.type { case .file: - mainMessage = String(format: (acceptAutomatically ? "DeviceCurrentlySendingFiles" : "DeviceSendingFiles").localized(), arguments: [device.name, fileStr]) + mainMessage = String(format: (acceptAutomatically ? "DeviceCurrentlySendingFiles" : "DeviceSendingFiles").localized(), arguments: [name, fileStr]) case .text: - mainMessage = String(format: (acceptAutomatically ? "DeviceCurrentlySendingText" : "DeviceSendingText").localized(), arguments: [device.name, fileStr]) + mainMessage = String(format: (acceptAutomatically ? "DeviceCurrentlySendingText" : "DeviceSendingText").localized(), arguments: [name, fileStr]) case .url: - mainMessage = String(format: (acceptAutomatically ? "DeviceCurrentlySendingUrl" : "DeviceSendingUrl").localized(), arguments: [device.name, fileStr]) + mainMessage = String(format: (acceptAutomatically ? "DeviceCurrentlySendingUrl" : "DeviceSendingUrl").localized(), arguments: [name, fileStr]) } diff --git a/QuickDrop/Assets.xcassets/QRBackground.imageset/Contents.json b/QuickDrop/Assets.xcassets/QRBackground.imageset/Contents.json new file mode 100644 index 0000000..46177de --- /dev/null +++ b/QuickDrop/Assets.xcassets/QRBackground.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "QRBackground.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/QuickDrop/Assets.xcassets/QRBackground.imageset/QRBackground.png b/QuickDrop/Assets.xcassets/QRBackground.imageset/QRBackground.png new file mode 100644 index 0000000000000000000000000000000000000000..055818fc65c548097f900fa38a530da16477f471 GIT binary patch literal 67475 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 literal 0 HcmV?d00001 diff --git a/ShareExtension/ShareViewController.swift b/ShareExtension/ShareViewController.swift index 5553a68..4172962 100644 --- a/ShareExtension/ShareViewController.swift +++ b/ShareExtension/ShareViewController.swift @@ -8,8 +8,10 @@ import Cocoa import Foundation import SwiftUI +import QRCode class ShareViewController: NSViewController, ShareExtensionDelegate { + private var urls: [URL] = [] private var textToSend: String? = nil private var foundDevices: [RemoteDeviceInfo] = [] @@ -154,7 +156,27 @@ class ShareViewController: NSViewController, ShareExtensionDelegate { private func openQrCodeView() { if qrCodeSheetView == nil { - let contentView = SmallSheetView(type: .sendToDeviceQrCode) { + + var image: Image? = nil + + do { + let qrKey = NearbyConnectionManager.shared.generateQrCodeKey() + let qrCodeImage = try QRCode.build + .text("https://quickshare.google/qrcode#key=\(qrKey)") + .backgroundColor(CGColor(srgbRed: 1, green: 1, blue: 1, alpha: 0)) + .quietZonePixelCount(3) + .onPixels.shape(.circle()) + .eye.shape(.squircle()) + .errorCorrection(.low) + .generate.image(dimension: 1000) + + image = Image(nsImage: NSImage(cgImage: qrCodeImage, size: qrCodeImage.size)) + } + catch { + log("Error generating QR code: \(error)") + } + + let contentView = SmallSheetView(type: .sendToDeviceQrCode, dynamicQrCode: image) { self.closeQrCodeView() } @@ -279,6 +301,12 @@ class ShareViewController: NSViewController, ShareExtensionDelegate { } + func startTransferWithQrCode(device: RemoteDeviceInfo) { + closeQrCodeView() + selectDevice(device: device) + } + + func connectionWasEstablished(pinCode: String) { connectionEstablished = true @@ -447,9 +475,10 @@ extension ShareViewController: NSCollectionViewDataSource { func getDeviceName(device: RemoteDeviceInfo) -> String { - if device.name.count <= 1 { - return "Android" + if let name = device.name, name.count > 1 { + return name } - return device.name + + return "Android" } } diff --git a/ShareExtension/View/SmallSheetView.swift b/ShareExtension/View/SmallSheetView.swift index 87bf426..212646e 100644 --- a/ShareExtension/View/SmallSheetView.swift +++ b/ShareExtension/View/SmallSheetView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import QRCode let smallSheetViewSize = CGSize(width: 530.0, height: 270.0) @@ -13,6 +14,7 @@ struct SmallSheetView: View { @State private var qrCode: String = "" let type: SheetViewType + var dynamicQrCode: Image? = nil let closeView: () -> Void @Environment(\.colorScheme) var colorScheme @@ -85,10 +87,18 @@ struct SmallSheetView: View { HStack { Spacer() - getImage() - .resizable() - .aspectRatio(1, contentMode: .fit) - .frame(height: 160) + ZStack { + if type == .sendToDeviceQrCode && dynamicQrCode != nil { + Image(.qrBackground) + .resizable() + .aspectRatio(1, contentMode: .fit) + } + + getImage() + .resizable() + .aspectRatio(1, contentMode: .fit) + } + .frame(height: 160) Spacer() @@ -138,7 +148,7 @@ struct SmallSheetView: View { func getImage() -> Image { switch type { case .sendToDeviceQrCode: - return connectWithApp ? Image(.qrApp) : Image(.QR) + return connectWithApp ? Image(.qrApp) : (dynamicQrCode ?? Image(.QR)) case .downloadAndroidApp: return Image(.qrApp) case .downloadCableConnectionApp: @@ -156,5 +166,25 @@ enum SheetViewType { #Preview { - SmallSheetView(type: .downloadCableConnectionApp, closeView: {}) + Preview() +} + + +private struct Preview: View { + var body: some View { + SmallSheetView(type: .sendToDeviceQrCode, dynamicQrCode: getImage(), closeView: {}) + } + + func getImage() -> Image { + let qrCodeImage = try! QRCode.build + .text("https://quickshare.google/qrcode#key=AAA") + .backgroundColor(CGColor(srgbRed: 1, green: 1, blue: 1, alpha: 0)) + .quietZonePixelCount(2) + .onPixels.shape(.circle()) + .eye.shape(.squircle()) + .errorCorrection(.low) + .generate.image(dimension: 1000) + + return Image(nsImage: NSImage(cgImage: qrCodeImage, size: qrCodeImage.size)) + } }