Add Welcome Screen, Tweak App Icon
No files matched your search
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 488 KiB |
|
After Width: | Height: | Size: 492 KiB |
|
After Width: | Height: | Size: 351 KiB |
|
After Width: | Height: | Size: 527 KiB |
|
After Width: | Height: | Size: 3.2 MiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 608 KiB |
|
After Width: | Height: | Size: 158 KiB |
|
After Width: | Height: | Size: 488 KiB |
|
After Width: | Height: | Size: 492 KiB |
|
After Width: | Height: | Size: 351 KiB |
|
After Width: | Height: | Size: 527 KiB |
@@ -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";
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
After Width: | Height: | Size: 752 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 159 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 364 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 1005 B |
|
Before Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 111 KiB |
|
Before Width: | Height: | Size: 111 KiB |
|
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"
|
||||
|
||||
|
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
|
||||
}
|
||||
}
|
||||
@@ -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.";
|
||||
@@ -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>
|
||||
@@ -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">
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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";
|
||||
|
After Width: | Height: | Size: 767 KiB |
|
Before Width: | Height: | Size: 1005 B |
|
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"
|
||||
},
|
||||
|
||||
@@ -27,3 +27,7 @@
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"UnsupportedType" = "نوع المرفق غير مدعوم";
|
||||
|
||||
"RecommendedApps" = "التطبيقات الموصى بها";
|
||||
"PrivacyPolicy" = "سياسة الخصوصية";
|
||||
"UserManual" = "دليل المستخدم";
|
||||
@@ -28,3 +28,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"UnsupportedType" = "不支持的文件类型";
|
||||
|
||||
"RecommendedApps" = "推荐应用";
|
||||
"PrivacyPolicy" = "隐私政策";
|
||||
"UserManual" = "用户手册";
|
||||