Add Welcome Screen, Tweak App Icon

This commit is contained in:
Leon Böttger committed 2025-01-04 19:39:11 +01:00
1 parent a39b6f0682
commit 90bfcda2f8
66 files changed
+475 -142

No files matched your search

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 527 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 608 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 527 KiB

Binary file not shown.
+26 -8
View File
@@ -40,6 +40,7 @@
69DCF49C2AB8C58500CBE2CC /* SwiftECC in Frameworks */ = {isa = PBXBuildFile; productRef = 69DCF49B2AB8C58500CBE2CC /* SwiftECC */; };
69DCF49D2AB8C9A500CBE2CC /* libNearbyShare.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 69DCF4812AB70D0600CBE2CC /* libNearbyShare.dylib */; };
69DCF49E2AB8C9A500CBE2CC /* libNearbyShare.dylib in Embed Libraries */ = {isa = PBXBuildFile; fileRef = 69DCF4812AB70D0600CBE2CC /* libNearbyShare.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
EBD2A2172D2860C100BCB8E2 /* WelcomeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBD2A2162D2860BE00BCB8E2 /* WelcomeScreen.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -208,6 +209,8 @@
E9C8C6D42CFF66650053474D /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/Localizable.strings; sourceTree = "<group>"; };
E9C8C6D52CFF66650053474D /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ro; path = ro.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
E9C8C6D62CFF66650053474D /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/Localizable.strings; sourceTree = "<group>"; };
EBD2A2162D2860BE00BCB8E2 /* WelcomeScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeScreen.swift; sourceTree = "<group>"; };
EBD2A2182D28619200BCB8E2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -290,7 +293,9 @@
69DA9A1029E0BF5100A442DA /* NearDrop */ = {
isa = PBXGroup;
children = (
EBD2A2182D28619200BCB8E2 /* Info.plist */,
69DA9A1129E0BF5100A442DA /* AppDelegate.swift */,
EBD2A2162D2860BE00BCB8E2 /* WelcomeScreen.swift */,
69DA9A1329E0BF5200A442DA /* Assets.xcassets */,
69D2C33629E78DF400EC7E30 /* Localizable.stringsdict */,
69D2C32B29E77F2200EC7E30 /* Localizable.strings */,
@@ -510,6 +515,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
EBD2A2172D2860C100BCB8E2 /* WelcomeScreen.swift in Sources */,
698DFB0329E362140064F247 /* NDNotificationCenterHackery.m in Sources */,
69DA9A1229E0BF5100A442DA /* AppDelegate.swift in Sources */,
);
@@ -679,7 +685,9 @@
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 6;
CURRENT_PROJECT_VERSION = 9;
DEVELOPMENT_TEAM = 92SDAWLN76;
ENABLE_HARDENED_RUNTIME = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = ShareExtension/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = NearDrop;
@@ -690,8 +698,8 @@
"@executable_path/../../../../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
MARKETING_VERSION = 2.0.2;
PRODUCT_BUNDLE_IDENTIFIER = me.grishka.NearDrop.ShareExtension;
MARKETING_VERSION = 2.1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.leonboettger.neardrop.ShareExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
@@ -705,7 +713,9 @@
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 6;
CURRENT_PROJECT_VERSION = 9;
DEVELOPMENT_TEAM = 92SDAWLN76;
ENABLE_HARDENED_RUNTIME = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = ShareExtension/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = NearDrop;
@@ -716,8 +726,8 @@
"@executable_path/../../../../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
MARKETING_VERSION = 2.0.2;
PRODUCT_BUNDLE_IDENTIFIER = me.grishka.NearDrop.ShareExtension;
MARKETING_VERSION = 2.1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.leonboettger.neardrop.ShareExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
@@ -851,7 +861,11 @@
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 9;
DEVELOPMENT_TEAM = 92SDAWLN76;
ENABLE_HARDENED_RUNTIME = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = NearDrop/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = NearDrop;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
INFOPLIST_KEY_LSUIElement = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
@@ -863,7 +877,7 @@
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
MARKETING_VERSION = 2.1.0;
PRODUCT_BUNDLE_IDENTIFIER = me.grishka.NearDrop;
PRODUCT_BUNDLE_IDENTIFIER = com.leonboettger.neardrop;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OBJC_BRIDGING_HEADER = "NearDrop/NearDrop-Bridging-Header.h";
@@ -883,7 +897,11 @@
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 9;
DEVELOPMENT_TEAM = 92SDAWLN76;
ENABLE_HARDENED_RUNTIME = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = NearDrop/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = NearDrop;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
INFOPLIST_KEY_LSUIElement = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
@@ -895,7 +913,7 @@
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
MARKETING_VERSION = 2.1.0;
PRODUCT_BUNDLE_IDENTIFIER = me.grishka.NearDrop;
PRODUCT_BUNDLE_IDENTIFIER = com.leonboettger.neardrop;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OBJC_BRIDGING_HEADER = "NearDrop/NearDrop-Bridging-Header.h";
+173 -118
View File
@@ -8,133 +8,188 @@
import Cocoa
import UserNotifications
import NearbyShare
import SwiftUI
@main
class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDelegate, MainAppDelegate{
private var statusItem:NSStatusItem?
private var activeIncomingTransfers:[String:TransferInfo]=[:]
private var statusItem:NSStatusItem?
private var activeIncomingTransfers:[String:TransferInfo]=[:]
var window: NSWindow?
func applicationDidFinishLaunching(_ aNotification: Notification) {
let menu=NSMenu()
menu.addItem(withTitle: NSLocalizedString("VisibleToEveryone", value: "Visible to everyone", comment: ""), action: nil, keyEquivalent: "")
menu.addItem(withTitle: String(format: NSLocalizedString("DeviceName", value: "Device name: %@", comment: ""), arguments: [Host.current().localizedName!]), action: nil, keyEquivalent: "")
menu.addItem(NSMenuItem.separator())
menu.addItem(withTitle: NSLocalizedString("Quit", value: "Quit NearDrop", comment: ""), action: #selector(NSApplication.terminate(_:)), keyEquivalent: "")
statusItem=NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
statusItem?.button?.image=NSImage(named: "MenuBarIcon")
statusItem?.menu=menu
statusItem?.behavior = .removalAllowed
let nc=UNUserNotificationCenter.current()
nc.requestAuthorization(options: [.alert, .sound]) { granted, err in
if !granted{
DispatchQueue.main.async {
self.showNotificationsDeniedAlert()
}
}
}
nc.delegate=self
let incomingTransfersCategory=UNNotificationCategory(identifier: "INCOMING_TRANSFERS", actions: [
UNNotificationAction(identifier: "ACCEPT", title: NSLocalizedString("Accept", comment: ""), options: UNNotificationActionOptions.authenticationRequired),
UNNotificationAction(identifier: "DECLINE", title: NSLocalizedString("Decline", comment: ""))
], intentIdentifiers: [])
let errorsCategory=UNNotificationCategory(identifier: "ERRORS", actions: [], intentIdentifiers: [])
nc.setNotificationCategories([incomingTransfersCategory, errorsCategory])
NearbyConnectionManager.shared.mainAppDelegate=self
NearbyConnectionManager.shared.becomeVisible()
}
func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
statusItem?.isVisible=true
return true
}
func applicationWillTerminate(_ aNotification: Notification) {
UNUserNotificationCenter.current().removeAllDeliveredNotifications()
let menu=NSMenu()
menu.addItem(withTitle: NSLocalizedString("VisibleToEveryone", value: "Visible to everyone", comment: ""), action: nil, keyEquivalent: "")
menu.addItem(withTitle: String(format: NSLocalizedString("DeviceName", value: "Device name: %@", comment: ""), arguments: [Host.current().localizedName!]), action: nil, keyEquivalent: "")
menu.addItem(NSMenuItem.separator())
// Add "Recommended Apps" menu item
let recommendedAppsItem = NSMenuItem(title: NSLocalizedString("RecommendedApps", value: "Recommended Apps", comment: ""), action: #selector(openRecommendedApps), keyEquivalent: "")
menu.addItem(recommendedAppsItem)
// Add "Privacy Policy" menu item
let privacyPolicyItem = NSMenuItem(title: NSLocalizedString("PrivacyPolicy", value: "Privacy Policy", comment: ""), action: #selector(openPrivacyPolicy), keyEquivalent: "")
menu.addItem(privacyPolicyItem)
menu.addItem(NSMenuItem.separator())
let userManualItem = NSMenuItem(title: NSLocalizedString("UserManual", value: "User Manual", comment: ""), action: #selector(openWelcomeScreen), keyEquivalent: "")
menu.addItem(userManualItem)
menu.addItem(withTitle: NSLocalizedString("Quit", value: "Quit NearDrop", comment: ""), action: #selector(NSApplication.terminate(_:)), keyEquivalent: "")
statusItem=NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
statusItem?.button?.image=NSImage(named: "MenuBarIcon")
statusItem?.menu=menu
statusItem?.behavior = .removalAllowed
let nc=UNUserNotificationCenter.current()
nc.requestAuthorization(options: [.alert, .sound]) { granted, err in
if !granted{
DispatchQueue.main.async {
self.showNotificationsDeniedAlert()
}
}
}
nc.delegate=self
let incomingTransfersCategory=UNNotificationCategory(identifier: "INCOMING_TRANSFERS", actions: [
UNNotificationAction(identifier: "ACCEPT", title: NSLocalizedString("Accept", comment: ""), options: UNNotificationActionOptions.authenticationRequired),
UNNotificationAction(identifier: "DECLINE", title: NSLocalizedString("Decline", comment: ""))
], intentIdentifiers: [])
let errorsCategory=UNNotificationCategory(identifier: "ERRORS", actions: [], intentIdentifiers: [])
nc.setNotificationCategories([incomingTransfersCategory, errorsCategory])
NearbyConnectionManager.shared.mainAppDelegate=self
NearbyConnectionManager.shared.becomeVisible()
openWelcomeScreen()
}
@objc func openWelcomeScreen() {
// Create the welcome screen SwiftUI view
let welcomeView = WelcomeScreen()
// Create an NSWindow to host the SwiftUI view
window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 400, height: 300),
styleMask: [.titled, .closable],
backing: .buffered,
defer: false
)
window?.center()
window?.isReleasedWhenClosed = false
window?.setFrameAutosaveName("WelcomeScreen")
window?.contentView = NSHostingView(rootView: welcomeView)
window?.makeKeyAndOrderFront(nil)
}
// Action for "Recommended Apps" menu item
@objc func openRecommendedApps() {
if let url = URL(string: "https://apps.apple.com/de/developer/leon-boettger/id1537384790") {
NSWorkspace.shared.open(url)
}
}
// Action for "Privacy Policy" menu item
@objc func openPrivacyPolicy() {
if let url = URL(string: " ") {
NSWorkspace.shared.open(url)
}
}
func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
statusItem?.isVisible=true
return true
}
func applicationWillTerminate(_ aNotification: Notification) {
UNUserNotificationCenter.current().removeAllDeliveredNotifications()
}
func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
return true
}
func showNotificationsDeniedAlert(){
let alert=NSAlert()
alert.alertStyle = .critical
alert.messageText=NSLocalizedString("NotificationsDenied.Title", value: "Notification Permission Required", comment: "")
alert.informativeText=NSLocalizedString("NotificationsDenied.Message", value: "NearDrop needs to be able to display notifications for incoming file transfers. Please allow notifications in System Settings.", comment: "")
alert.addButton(withTitle: NSLocalizedString("NotificationsDenied.OpenSettings", value: "Open settings", comment: ""))
alert.addButton(withTitle: NSLocalizedString("Quit", value: "Quit NearDrop", comment: ""))
let result=alert.runModal()
if result==NSApplication.ModalResponse.alertFirstButtonReturn{
NSWorkspace.shared.open(URL(string: "x-apple.systempreferences:com.apple.preference.notifications")!)
}else if result==NSApplication.ModalResponse.alertSecondButtonReturn{
NSApplication.shared.terminate(nil)
}
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
let transferID=response.notification.request.content.userInfo["transferID"]! as! String
NearbyConnectionManager.shared.submitUserConsent(transferID: transferID, accept: response.actionIdentifier=="ACCEPT")
if response.actionIdentifier != "ACCEPT"{
activeIncomingTransfers.removeValue(forKey: transferID)
}
completionHandler()
}
func obtainUserConsent(for transfer: TransferMetadata, from device: RemoteDeviceInfo) {
let fileStr:String
if let textTitle=transfer.textDescription{
fileStr=textTitle
}else if transfer.files.count==1{
fileStr=transfer.files[0].name
}else{
fileStr=String.localizedStringWithFormat(NSLocalizedString("NFiles", value: "%d files", comment: ""), transfer.files.count)
}
let notificationContent=UNMutableNotificationContent()
notificationContent.title="NearDrop"
notificationContent.subtitle=String(format:NSLocalizedString("PinCode", value: "PIN: %@", comment: ""), arguments: [transfer.pinCode!])
notificationContent.body=String(format: NSLocalizedString("DeviceSendingFiles", value: "%1$@ is sending you %2$@", comment: ""), arguments: [device.name, fileStr])
notificationContent.sound = .default
notificationContent.categoryIdentifier="INCOMING_TRANSFERS"
notificationContent.userInfo=["transferID": transfer.id]
if #available(macOS 11.0, *){
NDNotificationCenterHackery.removeDefaultAction(notificationContent)
}
let notificationReq=UNNotificationRequest(identifier: "transfer_"+transfer.id, content: notificationContent, trigger: nil)
UNUserNotificationCenter.current().add(notificationReq)
self.activeIncomingTransfers[transfer.id]=TransferInfo(device: device, transfer: transfer)
}
func incomingTransfer(id: String, didFinishWith error: Error?) {
guard let transfer=self.activeIncomingTransfers[id] else {return}
if let error=error{
let notificationContent=UNMutableNotificationContent()
notificationContent.title=String(format: NSLocalizedString("TransferError", value: "Failed to receive files from %@", comment: ""), arguments: [transfer.device.name])
if let ne=(error as? NearbyError){
switch ne{
case .inputOutput:
notificationContent.body="I/O Error";
case .protocolError(_):
notificationContent.body=NSLocalizedString("Error.Protocol", value: "Communication error", comment: "")
case .requiredFieldMissing:
notificationContent.body=NSLocalizedString("Error.Protocol", value: "Communication error", comment: "")
case .ukey2:
notificationContent.body=NSLocalizedString("Error.Crypto", value: "Encryption error", comment: "")
case .canceled(reason: _):
break; // can't happen for incoming transfers
}
}else{
notificationContent.body=error.localizedDescription
}
notificationContent.categoryIdentifier="ERRORS"
UNUserNotificationCenter.current().add(UNNotificationRequest(identifier: "transferError_"+id, content: notificationContent, trigger: nil))
}
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: ["transfer_"+id])
self.activeIncomingTransfers.removeValue(forKey: id)
}
func showNotificationsDeniedAlert(){
let alert=NSAlert()
alert.alertStyle = .critical
alert.messageText=NSLocalizedString("NotificationsDenied.Title", value: "Notification Permission Required", comment: "")
alert.informativeText=NSLocalizedString("NotificationsDenied.Message", value: "NearDrop needs to be able to display notifications for incoming file transfers. Please allow notifications in System Settings.", comment: "")
alert.addButton(withTitle: NSLocalizedString("NotificationsDenied.OpenSettings", value: "Open settings", comment: ""))
alert.addButton(withTitle: NSLocalizedString("Quit", value: "Quit NearDrop", comment: ""))
let result=alert.runModal()
if result==NSApplication.ModalResponse.alertFirstButtonReturn{
NSWorkspace.shared.open(URL(string: "x-apple.systempreferences:com.apple.preference.notifications")!)
}else if result==NSApplication.ModalResponse.alertSecondButtonReturn{
NSApplication.shared.terminate(nil)
}
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
let transferID=response.notification.request.content.userInfo["transferID"]! as! String
NearbyConnectionManager.shared.submitUserConsent(transferID: transferID, accept: response.actionIdentifier=="ACCEPT")
if response.actionIdentifier != "ACCEPT"{
activeIncomingTransfers.removeValue(forKey: transferID)
}
completionHandler()
}
func obtainUserConsent(for transfer: TransferMetadata, from device: RemoteDeviceInfo) {
let fileStr:String
if let textTitle=transfer.textDescription{
fileStr=textTitle
}else if transfer.files.count==1{
fileStr=transfer.files[0].name
}else{
fileStr=String.localizedStringWithFormat(NSLocalizedString("NFiles", value: "%d files", comment: ""), transfer.files.count)
}
let notificationContent=UNMutableNotificationContent()
notificationContent.title="NearDrop"
notificationContent.subtitle=String(format:NSLocalizedString("PinCode", value: "PIN: %@", comment: ""), arguments: [transfer.pinCode!])
notificationContent.body=String(format: NSLocalizedString("DeviceSendingFiles", value: "%1$@ is sending you %2$@", comment: ""), arguments: [device.name, fileStr])
notificationContent.sound = .default
notificationContent.categoryIdentifier="INCOMING_TRANSFERS"
notificationContent.userInfo=["transferID": transfer.id]
if #available(macOS 11.0, *){
NDNotificationCenterHackery.removeDefaultAction(notificationContent)
}
let notificationReq=UNNotificationRequest(identifier: "transfer_"+transfer.id, content: notificationContent, trigger: nil)
UNUserNotificationCenter.current().add(notificationReq)
self.activeIncomingTransfers[transfer.id]=TransferInfo(device: device, transfer: transfer)
}
func incomingTransfer(id: String, didFinishWith error: Error?) {
guard let transfer=self.activeIncomingTransfers[id] else {return}
if let error=error{
let notificationContent=UNMutableNotificationContent()
notificationContent.title=String(format: NSLocalizedString("TransferError", value: "Failed to receive files from %@", comment: ""), arguments: [transfer.device.name])
if let ne=(error as? NearbyError){
switch ne{
case .inputOutput:
notificationContent.body="I/O Error";
case .protocolError(_):
notificationContent.body=NSLocalizedString("Error.Protocol", value: "Communication error", comment: "")
case .requiredFieldMissing:
notificationContent.body=NSLocalizedString("Error.Protocol", value: "Communication error", comment: "")
case .ukey2:
notificationContent.body=NSLocalizedString("Error.Crypto", value: "Encryption error", comment: "")
case .canceled(reason: _):
break; // can't happen for incoming transfers
}
}else{
notificationContent.body=error.localizedDescription
}
notificationContent.categoryIdentifier="ERRORS"
UNUserNotificationCenter.current().add(UNNotificationRequest(identifier: "transferError_"+id, content: notificationContent, trigger: nil))
}
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: ["transfer_"+id])
self.activeIncomingTransfers.removeValue(forKey: id)
}
}
struct TransferInfo{
let device:RemoteDeviceInfo
let transfer:TransferMetadata
let device:RemoteDeviceInfo
let transfer:TransferMetadata
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 752 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 364 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1005 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

@@ -1,61 +1,61 @@
{
"images" : [
{
"filename" : "16.png",
"filename" : "1024-16.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "16x16"
},
{
"filename" : "32.png",
"filename" : "1024-32.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "16x16"
},
{
"filename" : "32 1.png",
"filename" : "1024-32.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "32x32"
},
{
"filename" : "64.png",
"filename" : "1024-64.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "32x32"
},
{
"filename" : "128.png",
"filename" : "1024-128.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "128x128"
},
{
"filename" : "256.png",
"filename" : "1024-256.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "128x128"
},
{
"filename" : "256 1.png",
"filename" : "1024-256.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "256x256"
},
{
"filename" : "512.png",
"filename" : "1024-512.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "256x256"
},
{
"filename" : "512 1.png",
"filename" : "1024-512.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "512x512"
},
{
"filename" : "1024.png",
"filename" : "1024-1024.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "512x512"
Binary file not shown.

After

Width:  |  Height:  |  Size: 767 KiB

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "1024.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
+12 -1
View File
@@ -1,4 +1,4 @@
/* No comment provided by engineer. */
/* No comment provided by engineer. */
"Accept" = "Accept";
/* No comment provided by engineer. */
@@ -40,3 +40,14 @@
/* No comment provided by engineer. */
"VisibleToEveryone" = "Visible to everyone";
"WelcomeToNearDrop" = "Welcome to NearDrop";
"Acknowledgements" = "Acknowledgements";
"UserManualDescription" = "When NearDrop is running, it will appear in the menu bar at the top of your screen. Click the NearDrop icon if you would like to stop receiving files.
To send files from your Android device to your Mac, just select \"Share\" or tap the Share Icon for a file you want to transfer, and select \"Quick Share\". Your Mac will be listed as an available device.
After that, you will see a notification on your Mac. Hover over the notification to accept or decline the file transfer.
Important: Your Android device and your Mac need to be on the same network to transfer files. If you are promted to allow incoming connections, please allow them.";
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
</dict>
</plist>
+2 -2
View File
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="22505" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="23504" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22505"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23504"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
+203
View File
@@ -0,0 +1,203 @@
//
// WelcomeScreen.swift
// NearDrop
//
// Created by Leon Böttger on 03.01.25.
//
import SwiftUI
struct WelcomeScreen: View {
@State private var newWindow: NSWindow? // Retain the NSWindow object
var body: some View {
ScrollView {
VStack {
Image("AppIconHighRes")
.resizable()
.frame(width: 150, height: 150)
.padding(.top, 50)
Text("WelcomeToNearDrop")
.font(.largeTitle)
.padding()
Text("UserManualDescription")
.multilineTextAlignment(.center)
.padding()
Button {
openNewWindow()
} label: {
Text("Acknowledgements")
.underline()
.font(.footnote)
.opacity(0.5)
}
.buttonStyle(.plain)
.padding()
}
.frame(maxWidth: 500)
.frame(maxWidth: .infinity)
}
.frame(width: 1000, height: 600)
}
func openNewWindow() {
// Create a new NSWindow and retain it in the `newWindow` property
let window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 500, height: 400),
styleMask: [.titled, .closable, .resizable],
backing: .buffered,
defer: false
)
window.center()
window.isReleasedWhenClosed = false
window.title = NSLocalizedString("Acknowledgements", value: "Acknowledgements", comment: "")
window.contentView = NSHostingView(rootView: LicensePage())
window.makeKeyAndOrderFront(nil)
// Retain the window to prevent deallocation
newWindow = window
}
}
struct LicensePage: View {
var body: some View {
ScrollView {
Text("""
NearDrop
https://github.com/grishka/NearDrop
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
ASN1
https://github.com/leif-ibsen/ASN1
Copyright (c) 2021 Leif Ibsen
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
BigInt
https://github.com/leif-ibsen/BigInt
Copyright (c) 2021 Leif Ibsen
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SwiftECC
https://github.com/leif-ibsen/SwiftECC
Copyright (c) 2021 Leif Ibsen
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
swift-protobuf
https://github.com/apple/swift-protobuf
Copyright 2024 Apple Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
""")
.padding()
}
}
}
#Preview {
WelcomeScreen()
}
+12 -1
View File
@@ -1,4 +1,4 @@
/* No comment provided by engineer. */
/* No comment provided by engineer. */
"Accept" = "Akzeptieren";
/* No comment provided by engineer. */
@@ -40,3 +40,14 @@
/* No comment provided by engineer. */
"VisibleToEveryone" = "Für alle sichtbar";
"WelcomeToNearDrop" = "Willkommen bei NearDrop";
"Acknowledgements" = "Danksagungen";
"UserManualDescription" = "Wenn NearDrop ausgeführt wird, erscheint es in der Menüleiste am oberen Rand Ihres Bildschirms. Klicken Sie auf das NearDrop-Symbol, wenn Sie den Empfang von Dateien beenden möchten.
Um Dateien von Ihrem Android-Gerät an Ihren Mac zu senden, wählen Sie einfach „Teilen“ oder tippen Sie auf das Freigabesymbol für eine Datei, die Sie übertragen möchten, und wählen Sie „Quick Share“. Ihr Mac wird als verfügbares Gerät aufgelistet.
Daraufhin wird eine Benachrichtigung auf Ihrem Mac angezeigt. Bewegen Sie den Mauszeiger über die Benachrichtigung, um die Dateiübertragung zu akzeptieren oder abzulehnen.
Wichtig: Ihr Android-Gerät und Ihr Mac müssen sich im selben Netzwerk befinden, um Dateien zu übertragen. Wenn Sie aufgefordert werden, eingehende Verbindungen zuzulassen, lassen Sie sie bitte zu";
Binary file not shown.

After

Width:  |  Height:  |  Size: 767 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1005 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

@@ -1,12 +1,11 @@
{
"images" : [
{
"filename" : "16.png",
"filename" : "1024.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "32.png",
"idiom" : "universal",
"scale" : "2x"
},
Binary file not shown.
@@ -27,3 +27,7 @@
/* No comment provided by engineer. */
"UnsupportedType" = "نوع المرفق غير مدعوم";
"RecommendedApps" = "التطبيقات الموصى بها";
"PrivacyPolicy" = "سياسة الخصوصية";
"UserManual" = "دليل المستخدم";
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -28,3 +28,6 @@
/* No comment provided by engineer. */
"UnsupportedType" = "不支持的文件类型";
"RecommendedApps" = "推荐应用";
"PrivacyPolicy" = "隐私政策";
"UserManual" = "用户手册";