Add ability to set custom device name

This commit is contained in:
Leon Böttger committed 2025-12-18 20:37:44 +01:00
1 parent 8aa3341595
commit a5e1030c1f
9 files changed
+3486 -2193

No files matched your search

@@ -41,15 +41,17 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb
private let hasConnectionMonitor = NWPathMonitor()
private let connectionMonitorQueue = DispatchQueue(label: "NetworkConnectionMonitorQueue")
private var securityScopeUrl: URL?
private static let customDeviceNameKey = "com.leonboettger.quickdrop.deviceName"
public let deviceInfo: EndpointInfo
public let endpointID: [UInt8] = generateEndpointID()
public static let maxNameChars = 31
public static let shared = NearbyConnectionManager()
@Published var attachments: AttachmentDetails? = nil
@Published var hasLocalNetworkPermission = true
@Published var isConnectedToLocalNetwork = true
@Published var deviceInfo: EndpointInfo
public var connectionUpdateCallback: (Bool) -> Void = { _ in } {
didSet {
@@ -57,17 +59,10 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb
}
}
public var changedDeviceNameCallback: () -> Void = { }
override init() {
#if os(macOS)
self.deviceInfo = EndpointInfo(name: Host.current().localizedName ?? "Mac", deviceType: .computer)
#else
let marketingName = Device.current.description.withoutBracketedContent
let isiPad = UIDevice.current.model == "iPad"
self.deviceInfo = EndpointInfo(name: marketingName, deviceType: isiPad ? .tablet : .phone)
#endif
self.deviceInfo = Self.getEndpointInfo()
tcpListener = try! NWListener(using: NWParameters(tls: .none))
super.init()
@@ -124,6 +119,49 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb
}
public static func getCustomDeviceName() -> String? {
let storedName = AppGroup.appGroupUD.string(forKey: Self.customDeviceNameKey)
guard let name = storedName, !name.isEmpty else {
return nil
}
return name
}
public func setCustomDeviceName(to newName: String) {
AppGroup.appGroupUD.setValue(newName, forKey: Self.customDeviceNameKey)
self.deviceInfo = Self.getEndpointInfo()
changedDeviceNameCallback()
if startedAdvertising {
self.becomeInvisible()
runAfter(seconds: 0.5) {
self.becomeVisible()
}
}
}
private static func getEndpointInfo() -> EndpointInfo {
let deviceType = isMac() ? RemoteDeviceInfo.DeviceType.computer : (isiPadOrMac() ? .tablet : .phone)
if let customName = getCustomDeviceName() {
return EndpointInfo(name: String(customName.prefix(Self.maxNameChars)), deviceType: deviceType)
}
#if os(macOS)
return EndpointInfo(name: String((Host.current().localizedName ?? "Mac").prefix(Self.maxNameChars)), deviceType: deviceType)
#else
return EndpointInfo(name: String(Device.current.description.withoutBracketedContent.prefix(Self.maxNameChars)), deviceType: deviceType)
#endif
}
public func becomeVisible() {
if startedAdvertising {
log("[NearbyConnectionManager] Already advertising, skipping")
+12 -4
View File
@@ -196,6 +196,9 @@
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 */; };
EBA1BB1D2EF1F5650068E46E /* ChangeDeviceNameModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBA1BB1C2EF1F5650068E46E /* ChangeDeviceNameModifier.swift */; };
EBA1BB1E2EF1F6030068E46E /* ChangeDeviceNameModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBA1BB1C2EF1F5650068E46E /* ChangeDeviceNameModifier.swift */; };
EBA1BB1F2EF1F6030068E46E /* ChangeDeviceNameModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBA1BB1C2EF1F5650068E46E /* ChangeDeviceNameModifier.swift */; };
EBADEB8F2E8029750021B5E4 /* DeviceKit in Frameworks */ = {isa = PBXBuildFile; productRef = EBADEB8E2E8029750021B5E4 /* DeviceKit */; };
EBADEB912E802A830021B5E4 /* DeviceKit in Frameworks */ = {isa = PBXBuildFile; productRef = EBADEB902E802A830021B5E4 /* DeviceKit */; };
EBBC93B22EC8B76600F541A1 /* LocalNetworkPermissionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBBC93B12EC8B75F00F541A1 /* LocalNetworkPermissionView.swift */; };
@@ -371,6 +374,7 @@
EB97A0162E51DF4F008B8C5C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
EB97A0192E51EA9C008B8C5C /* UIAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIAlert.swift; sourceTree = "<group>"; };
EB99257F2E45D53E00357055 /* sharing_enums.pb.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = sharing_enums.pb.swift; sourceTree = "<group>"; };
EBA1BB1C2EF1F5650068E46E /* ChangeDeviceNameModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangeDeviceNameModifier.swift; sourceTree = "<group>"; };
EBBC93B12EC8B75F00F541A1 /* LocalNetworkPermissionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalNetworkPermissionView.swift; sourceTree = "<group>"; };
EBBEE1452EC9D92F00AA7831 /* PhotoManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoManager.swift; sourceTree = "<group>"; };
EBC05C5A2E33936C00EECC18 /* ErrorAlertHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorAlertHandler.swift; sourceTree = "<group>"; };
@@ -643,6 +647,7 @@
EB7F54112D78E95C00989564 /* TutorialView.swift */,
EB7F54132D78E9E400989564 /* LicenseWindow.swift */,
EB869DB42E92AA33009AAAD4 /* QuickDropToastView.swift */,
EBA1BB1C2EF1F5650068E46E /* ChangeDeviceNameModifier.swift */,
EB05DFBD2E59FC8000EF567A /* TrustedDevicesView.swift */,
EB6F62D62D91F4AC00534EC2 /* SettingsView.swift */,
EBC05C812E33B58300EECC18 /* IssueViews */,
@@ -970,6 +975,7 @@
buildActionMask = 2147483647;
files = (
EBD2A2172D2860C100BCB8E2 /* WelcomeScreen.swift in Sources */,
EBA1BB1D2EF1F5650068E46E /* ChangeDeviceNameModifier.swift in Sources */,
EB3643602D73A2A2009CDFF8 /* AudioManager.swift in Sources */,
EB5C38412E38C49D0052EFD7 /* LaunchAtLogin.swift in Sources */,
EB5C37B82E377A6F0052EFD7 /* SleepManager.swift in Sources */,
@@ -1062,6 +1068,7 @@
EB5C37FF2E377CE70052EFD7 /* wire_format.pb.swift in Sources */,
EB950B792E521717002D68FA /* AttachmentDetails.swift in Sources */,
EB5C38002E377CE70052EFD7 /* DataModel.swift in Sources */,
EBA1BB1E2EF1F6030068E46E /* ChangeDeviceNameModifier.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1084,6 +1091,7 @@
EB950B502E520436002D68FA /* wire_format.pb.swift in Sources */,
EB950B582E5205B4002D68FA /* Zip.swift in Sources */,
EB950B512E520436002D68FA /* securemessage.pb.swift in Sources */,
EBA1BB1F2EF1F6030068E46E /* ChangeDeviceNameModifier.swift in Sources */,
EB950B522E520436002D68FA /* NearbyConnectionManager.swift in Sources */,
EB950B532E520436002D68FA /* ukey.pb.swift in Sources */,
EBBC93B22EC8B76600F541A1 /* LocalNetworkPermissionView.swift in Sources */,
@@ -1302,8 +1310,8 @@
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
SHARED_BUILD = 96;
SHARED_VERSION = 2.8;
SHARED_BUILD = 97;
SHARED_VERSION = 2.9;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_EMIT_LOC_STRINGS = YES;
@@ -1365,8 +1373,8 @@
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = macosx;
SHARED_BUILD = 96;
SHARED_VERSION = 2.8;
SHARED_BUILD = 97;
SHARED_VERSION = 2.9;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_EMIT_LOC_STRINGS = YES;
@@ -34,6 +34,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = "de"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
+4
View File
@@ -131,6 +131,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
}
}
NearbyConnectionManager.shared.changedDeviceNameCallback = {
self.visibleItem?.title = self.getDefaultVisibleLabel()
}
log("[AppDelegate] Application did finish launching")
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,55 @@
//
// ChangeDeviceNameModifier.swift
// QuickDrop
//
// Created by Leon Böttger on 16.12.25.
//
import SwiftUI
import LUI
struct ChangeDeviceNameModifier: ViewModifier {
let isEnabled: Bool
private let maxLength = NearbyConnectionManager.maxNameChars
@State private var isPresented = false
@State private var customName = ""
func body(content: Content) -> some View {
content
.onTapGesture {
guard isEnabled else { return }
lightVibration()
customName = NearbyConnectionManager.getCustomDeviceName() ?? ""
isPresented = true
}
.alert("ChangeDeviceName", isPresented: $isPresented) {
TextField("DeviceName", text: $customName)
.onChange(of: customName) { newValue in
if newValue.count > maxLength {
customName = String(newValue.prefix(maxLength))
}
}
Button("Save") {
NearbyConnectionManager.shared
.setCustomDeviceName(to: customName.trimmingCharacters(in: .whitespacesAndNewlines))
}
Button("Cancel", role: .cancel) { }
} message: {
Text("ChangeDeviceNameDescription")
}
}
}
extension View {
func changeDeviceNameAlert(isEnabled: Bool = true) -> some View {
modifier(ChangeDeviceNameModifier(isEnabled: isEnabled))
}
}
+16 -1
View File
@@ -12,6 +12,7 @@ import LUI
struct SettingsView: View {
@ObservedObject var settings = Settings.shared
@ObservedObject var connectionManager = NearbyConnectionManager.shared
var body: some View {
@@ -32,12 +33,26 @@ struct SettingsView: View {
Text("SaveFilesTo")
Spacer()
Text(getSavedFolderPath() ?? "DownloadsFolder".localized())
.foregroundColor((settings.saveFolderBookmark ?? Data()).isEmpty ? .gray : .primary)
.foregroundColor(.gray)
Button("SaveFilesToButton") {
selectFolder()
}
}
HStack {
Text("DeviceName")
Spacer()
Text(connectionManager.deviceInfo.name ?? "Unknown".localized())
.foregroundColor(.gray)
Button("Edit") {
}
.overlay(
Color.clear
.contentShape(Rectangle()).changeDeviceNameAlert()
)
}
Divider()
.padding(.vertical, 10)
+1 -1
View File
@@ -11,7 +11,7 @@ import Foundation
let configuration = LUIConfiguration(
appName: "QuickDrop",
plusVersionInfo: GetPlusViewInformation(lifetimeID: "plusversion", yearlyID: "quickdrop.yearly", plusViewHeader: isMac() ? "plusview_title" : nil, description: isMac() ? "plusview_description" : "plusview_description_phone", onBoughtPlusVersionAlertMessage: "plusview_success_description", plusVersionFeatures: [], settingsLabelColor: .orange, settingsFooter: "plusview_footer_phone"),
plusVersionInfo: GetPlusViewInformation(lifetimeID: "plusversion", monthlyID: "quickdrop.monthly", yearlyID: "quickdrop.yearly", plusViewHeader: isMac() ? "plusview_title" : nil, description: isMac() ? "plusview_description" : "plusview_description_phone", onBoughtPlusVersionAlertMessage: "plusview_success_description", plusVersionFeatures: [], settingsLabelColor: .orange, settingsFooter: "plusview_footer_phone"),
setPlusVersion: { result in
DispatchQueue.main.async {
+8
View File
@@ -76,11 +76,18 @@ struct DeviceListView: View {
LUIText(attachments?.shortDescription ?? NearbyConnectionManager.shared.deviceInfo.name ?? "Unknown".localized(), isBold: true)
.lineLimit(1)
if attachments == nil {
Image(systemName: "pencil")
.foregroundColor(.mainColor.opacity(0.5))
.padding(.horizontal, -3)
}
}
.padding(.horizontal, 16)
.padding(.vertical, 6)
.background(sheetActive ? Color.sheetForegroundColor : Color.defaultForegroundColor)
.cornerRadius(24)
.changeDeviceNameAlert(isEnabled: attachments == nil)
}
.padding(.horizontal)
.padding(.top, 8)
@@ -236,6 +243,7 @@ struct DeviceListView: View {
}
}
final class DeviceListViewModel: SharedInstance {
static var sharedInstance = DeviceListViewModel()