mirror of
https://github.com/grishka/NearDrop.git
synced 2026-04-03 01:36:15 +02:00
Merge branch 'master' into pt-BR-translation
This commit is contained in:
@@ -7,26 +7,63 @@
|
|||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
698DFAE629E2F91A0064F247 /* NearbyConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 698DFAE529E2F91A0064F247 /* NearbyConnection.swift */; };
|
691F53BB2ABB70840089FD92 /* DeviceListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 691F53B92ABB70840089FD92 /* DeviceListCell.swift */; };
|
||||||
|
691F53BC2ABB70840089FD92 /* DeviceListCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 691F53BA2ABB70840089FD92 /* DeviceListCell.xib */; };
|
||||||
|
691F53BE2ABF03820089FD92 /* OutboundNearbyConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 691F53BD2ABF03820089FD92 /* OutboundNearbyConnection.swift */; };
|
||||||
|
691F53C72AC2594E0089FD92 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 691F53C52AC2594E0089FD92 /* Localizable.strings */; };
|
||||||
|
691F53CB2AC2599B0089FD92 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 691F53C92AC2599B0089FD92 /* Localizable.stringsdict */; };
|
||||||
698DFB0329E362140064F247 /* NDNotificationCenterHackery.m in Sources */ = {isa = PBXBuildFile; fileRef = 698DFB0229E362140064F247 /* NDNotificationCenterHackery.m */; };
|
698DFB0329E362140064F247 /* NDNotificationCenterHackery.m in Sources */ = {isa = PBXBuildFile; fileRef = 698DFB0229E362140064F247 /* NDNotificationCenterHackery.m */; };
|
||||||
|
699B03452AB5FBA300E0D718 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 699B03442AB5FBA300E0D718 /* Assets.xcassets */; };
|
||||||
|
699DEBA62AB0573200115D22 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 699DEBA52AB0573200115D22 /* ShareViewController.swift */; };
|
||||||
|
699DEBA92AB0573200115D22 /* ShareViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 699DEBA72AB0573200115D22 /* ShareViewController.xib */; };
|
||||||
|
699DEBAE2AB0573200115D22 /* ShareExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 699DEBA12AB0573200115D22 /* ShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
69D2C32D29E77F2200EC7E30 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 69D2C32B29E77F2200EC7E30 /* Localizable.strings */; };
|
69D2C32D29E77F2200EC7E30 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 69D2C32B29E77F2200EC7E30 /* Localizable.strings */; };
|
||||||
69D2C32F29E7898C00EC7E30 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 69D2C32E29E7898C00EC7E30 /* MainMenu.xib */; };
|
69D2C32F29E7898C00EC7E30 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 69D2C32E29E7898C00EC7E30 /* MainMenu.xib */; };
|
||||||
69D2C33829E78DF400EC7E30 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 69D2C33629E78DF400EC7E30 /* Localizable.stringsdict */; };
|
69D2C33829E78DF400EC7E30 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 69D2C33629E78DF400EC7E30 /* Localizable.stringsdict */; };
|
||||||
69DA9A1229E0BF5100A442DA /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DA9A1129E0BF5100A442DA /* AppDelegate.swift */; };
|
69DA9A1229E0BF5100A442DA /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DA9A1129E0BF5100A442DA /* AppDelegate.swift */; };
|
||||||
69DA9A1429E0BF5200A442DA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 69DA9A1329E0BF5200A442DA /* Assets.xcassets */; };
|
69DA9A1429E0BF5200A442DA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 69DA9A1329E0BF5200A442DA /* Assets.xcassets */; };
|
||||||
69DA9A1F29E0C0B300A442DA /* NearbyConnectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DA9A1E29E0C0B300A442DA /* NearbyConnectionManager.swift */; };
|
69DCF48A2AB70E8C00CBE2CC /* wire_format.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DA9A2929E18CB500A442DA /* wire_format.pb.swift */; };
|
||||||
69DA9A2129E0CC4E00A442DA /* Data+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DA9A2029E0CC4E00A442DA /* Data+Extensions.swift */; };
|
69DCF48B2AB70E8C00CBE2CC /* ukey.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DA9A2829E18CB500A442DA /* ukey.pb.swift */; };
|
||||||
69DA9A2329E17F0400A442DA /* InboundNearbyConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DA9A2229E17F0400A442DA /* InboundNearbyConnection.swift */; };
|
69DCF48C2AB70E8C00CBE2CC /* device_to_device_messages.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DA9A2A29E18CB500A442DA /* device_to_device_messages.pb.swift */; };
|
||||||
69DA9A2629E189EF00A442DA /* SwiftProtobuf in Frameworks */ = {isa = PBXBuildFile; productRef = 69DA9A2529E189EF00A442DA /* SwiftProtobuf */; };
|
69DCF48D2AB70E8C00CBE2CC /* offline_wire_formats.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DA9A2B29E18CB500A442DA /* offline_wire_formats.pb.swift */; };
|
||||||
69DA9A2E29E18CB500A442DA /* ukey.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DA9A2829E18CB500A442DA /* ukey.pb.swift */; };
|
69DCF48E2AB70E8C00CBE2CC /* securegcm.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DA9A2C29E18CB500A442DA /* securegcm.pb.swift */; };
|
||||||
69DA9A2F29E18CB500A442DA /* wire_format.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DA9A2929E18CB500A442DA /* wire_format.pb.swift */; };
|
69DCF48F2AB70E8C00CBE2CC /* securemessage.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DA9A2D29E18CB500A442DA /* securemessage.pb.swift */; };
|
||||||
69DA9A3029E18CB500A442DA /* device_to_device_messages.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DA9A2A29E18CB500A442DA /* device_to_device_messages.pb.swift */; };
|
69DCF4902AB70E9700CBE2CC /* InboundNearbyConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DA9A2229E17F0400A442DA /* InboundNearbyConnection.swift */; };
|
||||||
69DA9A3129E18CB500A442DA /* offline_wire_formats.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DA9A2B29E18CB500A442DA /* offline_wire_formats.pb.swift */; };
|
69DCF4912AB70E9700CBE2CC /* NearbyConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 698DFAE529E2F91A0064F247 /* NearbyConnection.swift */; };
|
||||||
69DA9A3229E18CB500A442DA /* securegcm.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DA9A2C29E18CB500A442DA /* securegcm.pb.swift */; };
|
69DCF4922AB70E9700CBE2CC /* NearbyConnectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DA9A1E29E0C0B300A442DA /* NearbyConnectionManager.swift */; };
|
||||||
69DA9A3329E18CB500A442DA /* securemessage.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DA9A2D29E18CB500A442DA /* securemessage.pb.swift */; };
|
69DCF4932AB70E9700CBE2CC /* Data+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DA9A2029E0CC4E00A442DA /* Data+Extensions.swift */; };
|
||||||
69DA9A3629E1994C00A442DA /* SwiftECC in Frameworks */ = {isa = PBXBuildFile; productRef = 69DA9A3529E1994C00A442DA /* SwiftECC */; };
|
69DCF4942AB8BF7B00CBE2CC /* libNearbyShare.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 69DCF4812AB70D0600CBE2CC /* libNearbyShare.dylib */; };
|
||||||
|
69DCF4952AB8BF7B00CBE2CC /* libNearbyShare.dylib in Embed Libraries */ = {isa = PBXBuildFile; fileRef = 69DCF4812AB70D0600CBE2CC /* libNearbyShare.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||||
|
69DCF49A2AB8C58500CBE2CC /* SwiftProtobuf in Frameworks */ = {isa = PBXBuildFile; productRef = 69DCF4992AB8C58500CBE2CC /* SwiftProtobuf */; };
|
||||||
|
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, ); }; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
699DEBAC2AB0573200115D22 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = 69DA9A0629E0BF5100A442DA /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = 699DEBA02AB0573200115D22;
|
||||||
|
remoteInfo = ShareExtension;
|
||||||
|
};
|
||||||
|
69DCF4962AB8BF7B00CBE2CC /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = 69DA9A0629E0BF5100A442DA /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = 69DCF4802AB70D0600CBE2CC;
|
||||||
|
remoteInfo = NearbyShare;
|
||||||
|
};
|
||||||
|
69DCF49F2AB8C9A500CBE2CC /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = 69DA9A0629E0BF5100A442DA /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = 69DCF4802AB70D0600CBE2CC;
|
||||||
|
remoteInfo = NearbyShare;
|
||||||
|
};
|
||||||
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXCopyFilesBuildPhase section */
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
698DFAFF29E353220064F247 /* Embed Foundation Extensions */ = {
|
698DFAFF29E353220064F247 /* Embed Foundation Extensions */ = {
|
||||||
isa = PBXCopyFilesBuildPhase;
|
isa = PBXCopyFilesBuildPhase;
|
||||||
@@ -34,10 +71,43 @@
|
|||||||
dstPath = "";
|
dstPath = "";
|
||||||
dstSubfolderSpec = 13;
|
dstSubfolderSpec = 13;
|
||||||
files = (
|
files = (
|
||||||
|
699DEBAE2AB0573200115D22 /* ShareExtension.appex in Embed Foundation Extensions */,
|
||||||
);
|
);
|
||||||
name = "Embed Foundation Extensions";
|
name = "Embed Foundation Extensions";
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
699DEBDB2AB2828400115D22 /* Embed XPC Services */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = "$(CONTENTS_FOLDER_PATH)/XPCServices";
|
||||||
|
dstSubfolderSpec = 16;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
name = "Embed XPC Services";
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
69DCF4982AB8BF7B00CBE2CC /* Embed Libraries */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = "";
|
||||||
|
dstSubfolderSpec = 10;
|
||||||
|
files = (
|
||||||
|
69DCF4952AB8BF7B00CBE2CC /* libNearbyShare.dylib in Embed Libraries */,
|
||||||
|
);
|
||||||
|
name = "Embed Libraries";
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
69DCF4A12AB8C9A500CBE2CC /* Embed Libraries */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = "";
|
||||||
|
dstSubfolderSpec = 10;
|
||||||
|
files = (
|
||||||
|
69DCF49E2AB8C9A500CBE2CC /* libNearbyShare.dylib in Embed Libraries */,
|
||||||
|
);
|
||||||
|
name = "Embed Libraries";
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
@@ -47,12 +117,26 @@
|
|||||||
3226184E2A51E10600B06FD1 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
|
3226184E2A51E10600B06FD1 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
322618502A51EB8A00B06FD1 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = es; path = es.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
322618502A51EB8A00B06FD1 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = es; path = es.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
322618512A51EB8A00B06FD1 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
|
322618512A51EB8A00B06FD1 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
|
691F53B92ABB70840089FD92 /* DeviceListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceListCell.swift; sourceTree = "<group>"; };
|
||||||
|
691F53BA2ABB70840089FD92 /* DeviceListCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DeviceListCell.xib; sourceTree = "<group>"; };
|
||||||
|
691F53BD2ABF03820089FD92 /* OutboundNearbyConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutboundNearbyConnection.swift; sourceTree = "<group>"; };
|
||||||
|
691F53C42AC257A30089FD92 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/ShareViewController.strings; sourceTree = "<group>"; };
|
||||||
|
691F53C62AC2594E0089FD92 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
|
691F53C82AC259630089FD92 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
|
691F53CA2AC2599B0089FD92 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = Base; path = Base.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
|
691F53CC2AC259A20089FD92 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ru; path = ru.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
698DFAE529E2F91A0064F247 /* NearbyConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearbyConnection.swift; sourceTree = "<group>"; };
|
698DFAE529E2F91A0064F247 /* NearbyConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearbyConnection.swift; sourceTree = "<group>"; };
|
||||||
698DFAED29E353220064F247 /* UserNotifications.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotifications.framework; path = System/Library/Frameworks/UserNotifications.framework; sourceTree = SDKROOT; };
|
698DFAED29E353220064F247 /* UserNotifications.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotifications.framework; path = System/Library/Frameworks/UserNotifications.framework; sourceTree = SDKROOT; };
|
||||||
698DFAEF29E353220064F247 /* UserNotificationsUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotificationsUI.framework; path = System/Library/Frameworks/UserNotificationsUI.framework; sourceTree = SDKROOT; };
|
698DFAEF29E353220064F247 /* UserNotificationsUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotificationsUI.framework; path = System/Library/Frameworks/UserNotificationsUI.framework; sourceTree = SDKROOT; };
|
||||||
698DFB0029E362140064F247 /* NearDrop-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NearDrop-Bridging-Header.h"; sourceTree = "<group>"; };
|
698DFB0029E362140064F247 /* NearDrop-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NearDrop-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
698DFB0129E362140064F247 /* NDNotificationCenterHackery.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NDNotificationCenterHackery.h; sourceTree = "<group>"; };
|
698DFB0129E362140064F247 /* NDNotificationCenterHackery.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NDNotificationCenterHackery.h; sourceTree = "<group>"; };
|
||||||
698DFB0229E362140064F247 /* NDNotificationCenterHackery.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NDNotificationCenterHackery.m; sourceTree = "<group>"; };
|
698DFB0229E362140064F247 /* NDNotificationCenterHackery.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NDNotificationCenterHackery.m; sourceTree = "<group>"; };
|
||||||
|
699B03442AB5FBA300E0D718 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
|
699DEBA12AB0573200115D22 /* ShareExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = ShareExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
699DEBA52AB0573200115D22 /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = "<group>"; };
|
||||||
|
699DEBA82AB0573200115D22 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/ShareViewController.xib; sourceTree = "<group>"; };
|
||||||
|
699DEBAA2AB0573200115D22 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
699DEBAB2AB0573200115D22 /* ShareExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ShareExtension.entitlements; sourceTree = "<group>"; };
|
||||||
69D2C32C29E77F2200EC7E30 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = "<group>"; };
|
69D2C32C29E77F2200EC7E30 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
69D2C32E29E7898C00EC7E30 /* MainMenu.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MainMenu.xib; sourceTree = "<group>"; };
|
69D2C32E29E7898C00EC7E30 /* MainMenu.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MainMenu.xib; sourceTree = "<group>"; };
|
||||||
69D2C33029E789AF00EC7E30 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
|
69D2C33029E789AF00EC7E30 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
@@ -71,19 +155,38 @@
|
|||||||
69DA9A2B29E18CB500A442DA /* offline_wire_formats.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = offline_wire_formats.pb.swift; sourceTree = "<group>"; };
|
69DA9A2B29E18CB500A442DA /* offline_wire_formats.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = offline_wire_formats.pb.swift; sourceTree = "<group>"; };
|
||||||
69DA9A2C29E18CB500A442DA /* securegcm.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = securegcm.pb.swift; sourceTree = "<group>"; };
|
69DA9A2C29E18CB500A442DA /* securegcm.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = securegcm.pb.swift; sourceTree = "<group>"; };
|
||||||
69DA9A2D29E18CB500A442DA /* securemessage.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = securemessage.pb.swift; sourceTree = "<group>"; };
|
69DA9A2D29E18CB500A442DA /* securemessage.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = securemessage.pb.swift; sourceTree = "<group>"; };
|
||||||
|
69DCF4812AB70D0600CBE2CC /* libNearbyShare.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libNearbyShare.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
A3930F0529EAB2D1008F891D /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = it; path = it.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
A3930F0529EAB2D1008F891D /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = it; path = it.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
A3930F0629EAB2D1008F891D /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
|
A3930F0629EAB2D1008F891D /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
|
AE37AC272ABB62E400F0FF91 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = hu; path = hu.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
|
AE37AC282ABB62E400F0FF91 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
B670906E2A6D234D00DB8273 /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = th; path = th.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
B670906E2A6D234D00DB8273 /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = th; path = th.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
B670906F2A6D237600DB8273 /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/Localizable.strings; sourceTree = "<group>"; };
|
B670906F2A6D237600DB8273 /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
699DEB9E2AB0573200115D22 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
69DCF49D2AB8C9A500CBE2CC /* libNearbyShare.dylib in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
69DA9A0B29E0BF5100A442DA /* Frameworks */ = {
|
69DA9A0B29E0BF5100A442DA /* Frameworks */ = {
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
69DA9A3629E1994C00A442DA /* SwiftECC in Frameworks */,
|
69DCF4942AB8BF7B00CBE2CC /* libNearbyShare.dylib in Frameworks */,
|
||||||
69DA9A2629E189EF00A442DA /* SwiftProtobuf in Frameworks */,
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
69DCF47F2AB70D0600CBE2CC /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
69DCF49C2AB8C58500CBE2CC /* SwiftECC in Frameworks */,
|
||||||
|
69DCF49A2AB8C58500CBE2CC /* SwiftProtobuf in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -99,10 +202,28 @@
|
|||||||
name = Frameworks;
|
name = Frameworks;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
699DEBA22AB0573200115D22 /* ShareExtension */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
699DEBA52AB0573200115D22 /* ShareViewController.swift */,
|
||||||
|
691F53B92ABB70840089FD92 /* DeviceListCell.swift */,
|
||||||
|
691F53BA2ABB70840089FD92 /* DeviceListCell.xib */,
|
||||||
|
699DEBA72AB0573200115D22 /* ShareViewController.xib */,
|
||||||
|
699B03442AB5FBA300E0D718 /* Assets.xcassets */,
|
||||||
|
691F53C92AC2599B0089FD92 /* Localizable.stringsdict */,
|
||||||
|
691F53C52AC2594E0089FD92 /* Localizable.strings */,
|
||||||
|
699DEBAA2AB0573200115D22 /* Info.plist */,
|
||||||
|
699DEBAB2AB0573200115D22 /* ShareExtension.entitlements */,
|
||||||
|
);
|
||||||
|
path = ShareExtension;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
69DA9A0529E0BF5100A442DA = {
|
69DA9A0529E0BF5100A442DA = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
69DA9A1029E0BF5100A442DA /* NearDrop */,
|
69DA9A1029E0BF5100A442DA /* NearDrop */,
|
||||||
|
699DEBA22AB0573200115D22 /* ShareExtension */,
|
||||||
|
69DCF4822AB70D0600CBE2CC /* NearbyShare */,
|
||||||
698DFAEC29E353220064F247 /* Frameworks */,
|
698DFAEC29E353220064F247 /* Frameworks */,
|
||||||
69DA9A0F29E0BF5100A442DA /* Products */,
|
69DA9A0F29E0BF5100A442DA /* Products */,
|
||||||
);
|
);
|
||||||
@@ -112,6 +233,8 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
69DA9A0E29E0BF5100A442DA /* NearDrop.app */,
|
69DA9A0E29E0BF5100A442DA /* NearDrop.app */,
|
||||||
|
699DEBA12AB0573200115D22 /* ShareExtension.appex */,
|
||||||
|
69DCF4812AB70D0600CBE2CC /* libNearbyShare.dylib */,
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -119,17 +242,12 @@
|
|||||||
69DA9A1029E0BF5100A442DA /* NearDrop */ = {
|
69DA9A1029E0BF5100A442DA /* NearDrop */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
69DA9A2729E18CB500A442DA /* Protobuf */,
|
|
||||||
69DA9A1129E0BF5100A442DA /* AppDelegate.swift */,
|
69DA9A1129E0BF5100A442DA /* AppDelegate.swift */,
|
||||||
69DA9A1329E0BF5200A442DA /* Assets.xcassets */,
|
69DA9A1329E0BF5200A442DA /* Assets.xcassets */,
|
||||||
69DA9A1E29E0C0B300A442DA /* NearbyConnectionManager.swift */,
|
|
||||||
69D2C33629E78DF400EC7E30 /* Localizable.stringsdict */,
|
69D2C33629E78DF400EC7E30 /* Localizable.stringsdict */,
|
||||||
69D2C32B29E77F2200EC7E30 /* Localizable.strings */,
|
69D2C32B29E77F2200EC7E30 /* Localizable.strings */,
|
||||||
69DA9A2229E17F0400A442DA /* InboundNearbyConnection.swift */,
|
|
||||||
698DFB0129E362140064F247 /* NDNotificationCenterHackery.h */,
|
698DFB0129E362140064F247 /* NDNotificationCenterHackery.h */,
|
||||||
698DFB0229E362140064F247 /* NDNotificationCenterHackery.m */,
|
698DFB0229E362140064F247 /* NDNotificationCenterHackery.m */,
|
||||||
698DFAE529E2F91A0064F247 /* NearbyConnection.swift */,
|
|
||||||
69DA9A2029E0CC4E00A442DA /* Data+Extensions.swift */,
|
|
||||||
69DA9A1829E0BF5200A442DA /* NearDrop.entitlements */,
|
69DA9A1829E0BF5200A442DA /* NearDrop.entitlements */,
|
||||||
698DFB0029E362140064F247 /* NearDrop-Bridging-Header.h */,
|
698DFB0029E362140064F247 /* NearDrop-Bridging-Header.h */,
|
||||||
69D2C32E29E7898C00EC7E30 /* MainMenu.xib */,
|
69D2C32E29E7898C00EC7E30 /* MainMenu.xib */,
|
||||||
@@ -150,9 +268,51 @@
|
|||||||
path = Protobuf;
|
path = Protobuf;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
69DCF4822AB70D0600CBE2CC /* NearbyShare */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
69DA9A2729E18CB500A442DA /* Protobuf */,
|
||||||
|
69DA9A1E29E0C0B300A442DA /* NearbyConnectionManager.swift */,
|
||||||
|
69DA9A2229E17F0400A442DA /* InboundNearbyConnection.swift */,
|
||||||
|
691F53BD2ABF03820089FD92 /* OutboundNearbyConnection.swift */,
|
||||||
|
698DFAE529E2F91A0064F247 /* NearbyConnection.swift */,
|
||||||
|
69DA9A2029E0CC4E00A442DA /* Data+Extensions.swift */,
|
||||||
|
);
|
||||||
|
path = NearbyShare;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
|
/* Begin PBXHeadersBuildPhase section */
|
||||||
|
69DCF47D2AB70D0600CBE2CC /* Headers */ = {
|
||||||
|
isa = PBXHeadersBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXHeadersBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
|
699DEBA02AB0573200115D22 /* ShareExtension */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 699DEBB12AB0573200115D22 /* Build configuration list for PBXNativeTarget "ShareExtension" */;
|
||||||
|
buildPhases = (
|
||||||
|
699DEB9D2AB0573200115D22 /* Sources */,
|
||||||
|
699DEB9E2AB0573200115D22 /* Frameworks */,
|
||||||
|
699DEB9F2AB0573200115D22 /* Resources */,
|
||||||
|
69DCF4A12AB8C9A500CBE2CC /* Embed Libraries */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
69DCF4A02AB8C9A500CBE2CC /* PBXTargetDependency */,
|
||||||
|
);
|
||||||
|
name = ShareExtension;
|
||||||
|
productName = ShareExtension;
|
||||||
|
productReference = 699DEBA12AB0573200115D22 /* ShareExtension.appex */;
|
||||||
|
productType = "com.apple.product-type.app-extension";
|
||||||
|
};
|
||||||
69DA9A0D29E0BF5100A442DA /* NearDrop */ = {
|
69DA9A0D29E0BF5100A442DA /* NearDrop */ = {
|
||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 69DA9A1B29E0BF5200A442DA /* Build configuration list for PBXNativeTarget "NearDrop" */;
|
buildConfigurationList = 69DA9A1B29E0BF5200A442DA /* Build configuration list for PBXNativeTarget "NearDrop" */;
|
||||||
@@ -161,19 +321,42 @@
|
|||||||
69DA9A0B29E0BF5100A442DA /* Frameworks */,
|
69DA9A0B29E0BF5100A442DA /* Frameworks */,
|
||||||
69DA9A0C29E0BF5100A442DA /* Resources */,
|
69DA9A0C29E0BF5100A442DA /* Resources */,
|
||||||
698DFAFF29E353220064F247 /* Embed Foundation Extensions */,
|
698DFAFF29E353220064F247 /* Embed Foundation Extensions */,
|
||||||
|
699DEBDB2AB2828400115D22 /* Embed XPC Services */,
|
||||||
|
69DCF4982AB8BF7B00CBE2CC /* Embed Libraries */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
699DEBAD2AB0573200115D22 /* PBXTargetDependency */,
|
||||||
|
69DCF4972AB8BF7B00CBE2CC /* PBXTargetDependency */,
|
||||||
|
);
|
||||||
|
name = NearDrop;
|
||||||
|
packageProductDependencies = (
|
||||||
|
);
|
||||||
|
productName = NearDrop;
|
||||||
|
productReference = 69DA9A0E29E0BF5100A442DA /* NearDrop.app */;
|
||||||
|
productType = "com.apple.product-type.application";
|
||||||
|
};
|
||||||
|
69DCF4802AB70D0600CBE2CC /* NearbyShare */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 69DCF4892AB70D0600CBE2CC /* Build configuration list for PBXNativeTarget "NearbyShare" */;
|
||||||
|
buildPhases = (
|
||||||
|
69DCF47D2AB70D0600CBE2CC /* Headers */,
|
||||||
|
69DCF47E2AB70D0600CBE2CC /* Sources */,
|
||||||
|
69DCF47F2AB70D0600CBE2CC /* Frameworks */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
dependencies = (
|
dependencies = (
|
||||||
);
|
);
|
||||||
name = NearDrop;
|
name = NearbyShare;
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
69DA9A2529E189EF00A442DA /* SwiftProtobuf */,
|
69DCF4992AB8C58500CBE2CC /* SwiftProtobuf */,
|
||||||
69DA9A3529E1994C00A442DA /* SwiftECC */,
|
69DCF49B2AB8C58500CBE2CC /* SwiftECC */,
|
||||||
);
|
);
|
||||||
productName = NearDrop;
|
productName = NearbyShare;
|
||||||
productReference = 69DA9A0E29E0BF5100A442DA /* NearDrop.app */;
|
productReference = 69DCF4812AB70D0600CBE2CC /* libNearbyShare.dylib */;
|
||||||
productType = "com.apple.product-type.application";
|
productType = "com.apple.product-type.library.dynamic";
|
||||||
};
|
};
|
||||||
/* End PBXNativeTarget section */
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
@@ -185,10 +368,16 @@
|
|||||||
LastSwiftUpdateCheck = 1430;
|
LastSwiftUpdateCheck = 1430;
|
||||||
LastUpgradeCheck = 1430;
|
LastUpgradeCheck = 1430;
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
|
699DEBA02AB0573200115D22 = {
|
||||||
|
CreatedOnToolsVersion = 14.3.1;
|
||||||
|
};
|
||||||
69DA9A0D29E0BF5100A442DA = {
|
69DA9A0D29E0BF5100A442DA = {
|
||||||
CreatedOnToolsVersion = 14.3;
|
CreatedOnToolsVersion = 14.3;
|
||||||
LastSwiftMigration = 1430;
|
LastSwiftMigration = 1430;
|
||||||
};
|
};
|
||||||
|
69DCF4802AB70D0600CBE2CC = {
|
||||||
|
CreatedOnToolsVersion = 14.3.1;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
buildConfigurationList = 69DA9A0929E0BF5100A442DA /* Build configuration list for PBXProject "NearDrop" */;
|
buildConfigurationList = 69DA9A0929E0BF5100A442DA /* Build configuration list for PBXProject "NearDrop" */;
|
||||||
@@ -204,6 +393,7 @@
|
|||||||
es,
|
es,
|
||||||
th,
|
th,
|
||||||
"pt-BR",
|
"pt-BR",
|
||||||
|
hu,
|
||||||
);
|
);
|
||||||
mainGroup = 69DA9A0529E0BF5100A442DA;
|
mainGroup = 69DA9A0529E0BF5100A442DA;
|
||||||
packageReferences = (
|
packageReferences = (
|
||||||
@@ -215,11 +405,25 @@
|
|||||||
projectRoot = "";
|
projectRoot = "";
|
||||||
targets = (
|
targets = (
|
||||||
69DA9A0D29E0BF5100A442DA /* NearDrop */,
|
69DA9A0D29E0BF5100A442DA /* NearDrop */,
|
||||||
|
699DEBA02AB0573200115D22 /* ShareExtension */,
|
||||||
|
69DCF4802AB70D0600CBE2CC /* NearbyShare */,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
/* End PBXProject section */
|
/* End PBXProject section */
|
||||||
|
|
||||||
/* Begin PBXResourcesBuildPhase section */
|
/* Begin PBXResourcesBuildPhase section */
|
||||||
|
699DEB9F2AB0573200115D22 /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
699B03452AB5FBA300E0D718 /* Assets.xcassets in Resources */,
|
||||||
|
691F53C72AC2594E0089FD92 /* Localizable.strings in Resources */,
|
||||||
|
691F53BC2ABB70840089FD92 /* DeviceListCell.xib in Resources */,
|
||||||
|
691F53CB2AC2599B0089FD92 /* Localizable.stringsdict in Resources */,
|
||||||
|
699DEBA92AB0573200115D22 /* ShareViewController.xib in Resources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
69DA9A0C29E0BF5100A442DA /* Resources */ = {
|
69DA9A0C29E0BF5100A442DA /* Resources */ = {
|
||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@@ -234,28 +438,90 @@
|
|||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
699DEB9D2AB0573200115D22 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
699DEBA62AB0573200115D22 /* ShareViewController.swift in Sources */,
|
||||||
|
691F53BB2ABB70840089FD92 /* DeviceListCell.swift in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
69DA9A0A29E0BF5100A442DA /* Sources */ = {
|
69DA9A0A29E0BF5100A442DA /* Sources */ = {
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
69DA9A2E29E18CB500A442DA /* ukey.pb.swift in Sources */,
|
|
||||||
698DFB0329E362140064F247 /* NDNotificationCenterHackery.m in Sources */,
|
698DFB0329E362140064F247 /* NDNotificationCenterHackery.m in Sources */,
|
||||||
69DA9A2329E17F0400A442DA /* InboundNearbyConnection.swift in Sources */,
|
|
||||||
69DA9A1F29E0C0B300A442DA /* NearbyConnectionManager.swift in Sources */,
|
|
||||||
69DA9A3029E18CB500A442DA /* device_to_device_messages.pb.swift in Sources */,
|
|
||||||
69DA9A3129E18CB500A442DA /* offline_wire_formats.pb.swift in Sources */,
|
|
||||||
69DA9A1229E0BF5100A442DA /* AppDelegate.swift in Sources */,
|
69DA9A1229E0BF5100A442DA /* AppDelegate.swift in Sources */,
|
||||||
69DA9A2F29E18CB500A442DA /* wire_format.pb.swift in Sources */,
|
);
|
||||||
69DA9A3329E18CB500A442DA /* securemessage.pb.swift in Sources */,
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
69DA9A2129E0CC4E00A442DA /* Data+Extensions.swift in Sources */,
|
};
|
||||||
69DA9A3229E18CB500A442DA /* securegcm.pb.swift in Sources */,
|
69DCF47E2AB70D0600CBE2CC /* Sources */ = {
|
||||||
698DFAE629E2F91A0064F247 /* NearbyConnection.swift in Sources */,
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
69DCF48D2AB70E8C00CBE2CC /* offline_wire_formats.pb.swift in Sources */,
|
||||||
|
69DCF48F2AB70E8C00CBE2CC /* securemessage.pb.swift in Sources */,
|
||||||
|
69DCF4912AB70E9700CBE2CC /* NearbyConnection.swift in Sources */,
|
||||||
|
69DCF4922AB70E9700CBE2CC /* NearbyConnectionManager.swift in Sources */,
|
||||||
|
69DCF48E2AB70E8C00CBE2CC /* securegcm.pb.swift in Sources */,
|
||||||
|
691F53BE2ABF03820089FD92 /* OutboundNearbyConnection.swift in Sources */,
|
||||||
|
69DCF48C2AB70E8C00CBE2CC /* device_to_device_messages.pb.swift in Sources */,
|
||||||
|
69DCF4902AB70E9700CBE2CC /* InboundNearbyConnection.swift in Sources */,
|
||||||
|
69DCF48B2AB70E8C00CBE2CC /* ukey.pb.swift in Sources */,
|
||||||
|
69DCF48A2AB70E8C00CBE2CC /* wire_format.pb.swift in Sources */,
|
||||||
|
69DCF4932AB70E9700CBE2CC /* Data+Extensions.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
/* End PBXSourcesBuildPhase section */
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXTargetDependency section */
|
||||||
|
699DEBAD2AB0573200115D22 /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = 699DEBA02AB0573200115D22 /* ShareExtension */;
|
||||||
|
targetProxy = 699DEBAC2AB0573200115D22 /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
|
69DCF4972AB8BF7B00CBE2CC /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = 69DCF4802AB70D0600CBE2CC /* NearbyShare */;
|
||||||
|
targetProxy = 69DCF4962AB8BF7B00CBE2CC /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
|
69DCF4A02AB8C9A500CBE2CC /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = 69DCF4802AB70D0600CBE2CC /* NearbyShare */;
|
||||||
|
targetProxy = 69DCF49F2AB8C9A500CBE2CC /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
/* Begin PBXVariantGroup section */
|
/* Begin PBXVariantGroup section */
|
||||||
|
691F53C52AC2594E0089FD92 /* Localizable.strings */ = {
|
||||||
|
isa = PBXVariantGroup;
|
||||||
|
children = (
|
||||||
|
691F53C62AC2594E0089FD92 /* Base */,
|
||||||
|
691F53C82AC259630089FD92 /* ru */,
|
||||||
|
);
|
||||||
|
name = Localizable.strings;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
691F53C92AC2599B0089FD92 /* Localizable.stringsdict */ = {
|
||||||
|
isa = PBXVariantGroup;
|
||||||
|
children = (
|
||||||
|
691F53CA2AC2599B0089FD92 /* Base */,
|
||||||
|
691F53CC2AC259A20089FD92 /* ru */,
|
||||||
|
);
|
||||||
|
name = Localizable.stringsdict;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
699DEBA72AB0573200115D22 /* ShareViewController.xib */ = {
|
||||||
|
isa = PBXVariantGroup;
|
||||||
|
children = (
|
||||||
|
699DEBA82AB0573200115D22 /* Base */,
|
||||||
|
691F53C42AC257A30089FD92 /* ru */,
|
||||||
|
);
|
||||||
|
name = ShareViewController.xib;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
69D2C32B29E77F2200EC7E30 /* Localizable.strings */ = {
|
69D2C32B29E77F2200EC7E30 /* Localizable.strings */ = {
|
||||||
isa = PBXVariantGroup;
|
isa = PBXVariantGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -266,6 +532,7 @@
|
|||||||
322618512A51EB8A00B06FD1 /* es */,
|
322618512A51EB8A00B06FD1 /* es */,
|
||||||
B670906F2A6D237600DB8273 /* th */,
|
B670906F2A6D237600DB8273 /* th */,
|
||||||
2C284C442AB9FFA200F8D624 /* pt-BR */,
|
2C284C442AB9FFA200F8D624 /* pt-BR */,
|
||||||
|
AE37AC282ABB62E400F0FF91 /* hu */,
|
||||||
);
|
);
|
||||||
name = Localizable.strings;
|
name = Localizable.strings;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -280,6 +547,7 @@
|
|||||||
322618502A51EB8A00B06FD1 /* es */,
|
322618502A51EB8A00B06FD1 /* es */,
|
||||||
B670906E2A6D234D00DB8273 /* th */,
|
B670906E2A6D234D00DB8273 /* th */,
|
||||||
2C284C432AB9FFA200F8D624 /* pt-BR */,
|
2C284C432AB9FFA200F8D624 /* pt-BR */,
|
||||||
|
AE37AC272ABB62E400F0FF91 /* hu */,
|
||||||
);
|
);
|
||||||
name = Localizable.stringsdict;
|
name = Localizable.stringsdict;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -287,6 +555,58 @@
|
|||||||
/* End PBXVariantGroup section */
|
/* End PBXVariantGroup section */
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
/* Begin XCBuildConfiguration section */
|
||||||
|
699DEBAF2AB0573200115D22 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = ShareExtension/Info.plist;
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = NearDrop;
|
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/../Frameworks",
|
||||||
|
"@executable_path/../../../../Frameworks",
|
||||||
|
);
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = me.grishka.NearDrop.ShareExtension;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
699DEBB02AB0573200115D22 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = ShareExtension/Info.plist;
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = NearDrop;
|
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/../Frameworks",
|
||||||
|
"@executable_path/../../../../Frameworks",
|
||||||
|
);
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = me.grishka.NearDrop.ShareExtension;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
69DA9A1929E0BF5200A442DA /* Debug */ = {
|
69DA9A1929E0BF5200A442DA /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
@@ -338,7 +658,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 13.3;
|
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
@@ -393,7 +713,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 13.3;
|
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
@@ -405,6 +725,7 @@
|
|||||||
69DA9A1C29E0BF5200A442DA /* Debug */ = {
|
69DA9A1C29E0BF5200A442DA /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
@@ -436,6 +757,7 @@
|
|||||||
69DA9A1D29E0BF5200A442DA /* Release */ = {
|
69DA9A1D29E0BF5200A442DA /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
@@ -463,9 +785,46 @@
|
|||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
|
69DCF4872AB70D0600CBE2CC /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||||
|
DYLIB_CURRENT_VERSION = 1;
|
||||||
|
EXECUTABLE_PREFIX = lib;
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
69DCF4882AB70D0600CBE2CC /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||||
|
DYLIB_CURRENT_VERSION = 1;
|
||||||
|
EXECUTABLE_PREFIX = lib;
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
/* End XCBuildConfiguration section */
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
/* Begin XCConfigurationList section */
|
||||||
|
699DEBB12AB0573200115D22 /* Build configuration list for PBXNativeTarget "ShareExtension" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
699DEBAF2AB0573200115D22 /* Debug */,
|
||||||
|
699DEBB02AB0573200115D22 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
69DA9A0929E0BF5100A442DA /* Build configuration list for PBXProject "NearDrop" */ = {
|
69DA9A0929E0BF5100A442DA /* Build configuration list for PBXProject "NearDrop" */ = {
|
||||||
isa = XCConfigurationList;
|
isa = XCConfigurationList;
|
||||||
buildConfigurations = (
|
buildConfigurations = (
|
||||||
@@ -484,6 +843,15 @@
|
|||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
|
69DCF4892AB70D0600CBE2CC /* Build configuration list for PBXNativeTarget "NearbyShare" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
69DCF4872AB70D0600CBE2CC /* Debug */,
|
||||||
|
69DCF4882AB70D0600CBE2CC /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
/* Begin XCRemoteSwiftPackageReference section */
|
/* Begin XCRemoteSwiftPackageReference section */
|
||||||
@@ -506,12 +874,12 @@
|
|||||||
/* End XCRemoteSwiftPackageReference section */
|
/* End XCRemoteSwiftPackageReference section */
|
||||||
|
|
||||||
/* Begin XCSwiftPackageProductDependency section */
|
/* Begin XCSwiftPackageProductDependency section */
|
||||||
69DA9A2529E189EF00A442DA /* SwiftProtobuf */ = {
|
69DCF4992AB8C58500CBE2CC /* SwiftProtobuf */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
package = 69DA9A2429E189EF00A442DA /* XCRemoteSwiftPackageReference "swift-protobuf" */;
|
package = 69DA9A2429E189EF00A442DA /* XCRemoteSwiftPackageReference "swift-protobuf" */;
|
||||||
productName = SwiftProtobuf;
|
productName = SwiftProtobuf;
|
||||||
};
|
};
|
||||||
69DA9A3529E1994C00A442DA /* SwiftECC */ = {
|
69DCF49B2AB8C58500CBE2CC /* SwiftECC */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
package = 69DA9A3429E1994C00A442DA /* XCRemoteSwiftPackageReference "SwiftECC" */;
|
package = 69DA9A3429E1994C00A442DA /* XCRemoteSwiftPackageReference "SwiftECC" */;
|
||||||
productName = SwiftECC;
|
productName = SwiftECC;
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "1430"
|
||||||
|
wasCreatedForAppExtension = "YES"
|
||||||
|
version = "2.0">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "699DEBA02AB0573200115D22"
|
||||||
|
BuildableName = "ShareExtension.appex"
|
||||||
|
BlueprintName = "ShareExtension"
|
||||||
|
ReferencedContainer = "container:NearDrop.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "69DA9A0D29E0BF5100A442DA"
|
||||||
|
BuildableName = "NearDrop.app"
|
||||||
|
BlueprintName = "NearDrop"
|
||||||
|
ReferencedContainer = "container:NearDrop.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
shouldAutocreateTestPlan = "YES">
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = ""
|
||||||
|
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
|
||||||
|
launchStyle = "0"
|
||||||
|
askForAppToLaunch = "Yes"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES"
|
||||||
|
launchAutomaticallySubstyle = "2">
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "69DA9A0D29E0BF5100A442DA"
|
||||||
|
BuildableName = "NearDrop.app"
|
||||||
|
BlueprintName = "NearDrop"
|
||||||
|
ReferencedContainer = "container:NearDrop.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
askForAppToLaunch = "Yes"
|
||||||
|
launchAutomaticallySubstyle = "2">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "69DA9A0D29E0BF5100A442DA"
|
||||||
|
BuildableName = "NearDrop.app"
|
||||||
|
BlueprintName = "NearDrop"
|
||||||
|
ReferencedContainer = "container:NearDrop.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
||||||
@@ -7,12 +7,12 @@
|
|||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
import UserNotifications
|
import UserNotifications
|
||||||
|
import NearbyShare
|
||||||
|
|
||||||
@main
|
@main
|
||||||
class AppDelegate: NSObject, NSApplicationDelegate{
|
class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDelegate, MainAppDelegate{
|
||||||
|
|
||||||
private var connectionManager:NearbyConnectionManager?
|
|
||||||
private var statusItem:NSStatusItem?
|
private var statusItem:NSStatusItem?
|
||||||
|
private var activeIncomingTransfers:[String:TransferInfo]=[:]
|
||||||
|
|
||||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||||
let menu=NSMenu()
|
let menu=NSMenu()
|
||||||
@@ -32,11 +32,13 @@ class AppDelegate: NSObject, NSApplicationDelegate{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
nc.delegate=self
|
||||||
let incomingTransfersCategory=NDNotificationCenterHackery.hackedNotificationCategory()
|
let incomingTransfersCategory=NDNotificationCenterHackery.hackedNotificationCategory()
|
||||||
let errorsCategory=UNNotificationCategory(identifier: "ERRORS", actions: [], intentIdentifiers: [])
|
let errorsCategory=UNNotificationCategory(identifier: "ERRORS", actions: [], intentIdentifiers: [])
|
||||||
nc.setNotificationCategories([incomingTransfersCategory, errorsCategory])
|
nc.setNotificationCategories([incomingTransfersCategory, errorsCategory])
|
||||||
connectionManager=NearbyConnectionManager()
|
NearbyConnectionManager.shared.mainAppDelegate=self
|
||||||
}
|
NearbyConnectionManager.shared.becomeVisible()
|
||||||
|
}
|
||||||
|
|
||||||
func applicationWillTerminate(_ aNotification: Notification) {
|
func applicationWillTerminate(_ aNotification: Notification) {
|
||||||
UNUserNotificationCenter.current().removeAllDeliveredNotifications()
|
UNUserNotificationCenter.current().removeAllDeliveredNotifications()
|
||||||
@@ -60,5 +62,66 @@ class AppDelegate: NSObject, NSApplicationDelegate{
|
|||||||
NSApplication.shared.terminate(nil)
|
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 notificationContent=UNMutableNotificationContent()
|
||||||
|
notificationContent.title="NearDrop"
|
||||||
|
notificationContent.subtitle=String(format:NSLocalizedString("PinCode", value: "PIN: %@", comment: ""), arguments: [transfer.pinCode!])
|
||||||
|
let fileStr:String
|
||||||
|
if transfer.files.count==1{
|
||||||
|
fileStr=transfer.files[0].name
|
||||||
|
}else{
|
||||||
|
fileStr=String.localizedStringWithFormat(NSLocalizedString("NFiles", value: "%d files", comment: ""), transfer.files.count)
|
||||||
|
}
|
||||||
|
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]
|
||||||
|
NDNotificationCenterHackery.removeDefaultAction(notificationContent)
|
||||||
|
let notificationReq=UNNotificationRequest(identifier: "transfer_"+transfer.id, content: notificationContent, trigger: nil)
|
||||||
|
self.activeIncomingTransfers[transfer.id]=TransferInfo(device: device, transfer: transfer)
|
||||||
|
UNUserNotificationCenter.current().add(notificationReq)
|
||||||
|
}
|
||||||
|
|
||||||
|
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(let er):
|
||||||
|
notificationContent.body=er.localizedDescription
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,134 +0,0 @@
|
|||||||
//
|
|
||||||
// NearbyConnectionManager.swift
|
|
||||||
// NearDrop
|
|
||||||
//
|
|
||||||
// Created by Grishka on 08.04.2023.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import Network
|
|
||||||
import UserNotifications
|
|
||||||
|
|
||||||
class NearbyConnectionManager : NSObject, NetServiceDelegate, InboundNearbyConnectionDelegate, UNUserNotificationCenterDelegate{
|
|
||||||
|
|
||||||
private var tcpListener:NWListener;
|
|
||||||
private let endpointID:[UInt8]=generateEndpointID()
|
|
||||||
private var mdnsService:NetService?
|
|
||||||
private var activeConnections:[String:InboundNearbyConnection]=[:]
|
|
||||||
|
|
||||||
override init() {
|
|
||||||
tcpListener=try! NWListener(using: NWParameters(tls: .none))
|
|
||||||
super.init()
|
|
||||||
UNUserNotificationCenter.current().delegate=self
|
|
||||||
startTCPListener()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func startTCPListener(){
|
|
||||||
tcpListener.stateUpdateHandler={(state:NWListener.State) in
|
|
||||||
if case .ready = state {
|
|
||||||
self.initMDNS()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tcpListener.newConnectionHandler={(connection:NWConnection) in
|
|
||||||
let id=UUID().uuidString
|
|
||||||
let conn=InboundNearbyConnection(connection: connection, id: id)
|
|
||||||
self.activeConnections[id]=conn
|
|
||||||
conn.delegate=self
|
|
||||||
conn.start()
|
|
||||||
}
|
|
||||||
tcpListener.start(queue: .global(qos: .utility))
|
|
||||||
}
|
|
||||||
|
|
||||||
private static func generateEndpointID()->[UInt8]{
|
|
||||||
var id:[UInt8]=[]
|
|
||||||
let alphabet="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".compactMap {UInt8($0.asciiValue!)}
|
|
||||||
for _ in 0...3{
|
|
||||||
id.append(alphabet[Int.random(in: 0..<alphabet.count)])
|
|
||||||
}
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
|
|
||||||
private func initMDNS(){
|
|
||||||
let nameBytes:[UInt8]=[
|
|
||||||
0x23, // PCP
|
|
||||||
endpointID[0], endpointID[1], endpointID[2], endpointID[3],
|
|
||||||
0xFC, 0x9F, 0x5E, // Service ID hash
|
|
||||||
0, 0
|
|
||||||
]
|
|
||||||
let name=Data(nameBytes).urlSafeBase64EncodedString()
|
|
||||||
// 1 byte: Version(3 bits)|Visibility(1 bit)|Device Type(3 bits)|Reserved(1 bits)
|
|
||||||
// Device types: unknown=0, phone=1, tablet=2, laptop=3
|
|
||||||
var endpointInfo:[UInt8]=[3 << 1]
|
|
||||||
// 16 bytes: unknown random bytes
|
|
||||||
for _ in 0...15{
|
|
||||||
endpointInfo.append(UInt8.random(in: 0...255))
|
|
||||||
}
|
|
||||||
// Device name in UTF-8 prefixed with 1-byte length
|
|
||||||
let hostName=Host.current().localizedName!
|
|
||||||
let hostNameChars=hostName.utf8
|
|
||||||
endpointInfo.append(UInt8(hostNameChars.count))
|
|
||||||
for (i, ch) in hostNameChars.enumerated(){
|
|
||||||
guard i<256 else {break}
|
|
||||||
endpointInfo.append(UInt8(ch))
|
|
||||||
}
|
|
||||||
|
|
||||||
let port:Int32=Int32(tcpListener.port!.rawValue)
|
|
||||||
mdnsService=NetService(domain: "", type: "_FC9F5ED42C8A._tcp.", name: name, port: port)
|
|
||||||
mdnsService?.delegate=self
|
|
||||||
mdnsService?.includesPeerToPeer=true
|
|
||||||
mdnsService?.setTXTRecord(NetService.data(fromTXTRecord: [
|
|
||||||
"n": Data(endpointInfo).urlSafeBase64EncodedString().data(using: .utf8)!
|
|
||||||
]))
|
|
||||||
mdnsService?.publish()
|
|
||||||
}
|
|
||||||
|
|
||||||
func obtainUserConsent(for transfer: TransferMetadata, from device: RemoteDeviceInfo, connection: InboundNearbyConnection) {
|
|
||||||
let notificationContent=UNMutableNotificationContent()
|
|
||||||
notificationContent.title="NearDrop"
|
|
||||||
notificationContent.subtitle=String(format:NSLocalizedString("PinCode", value: "PIN: %@", comment: ""), arguments: [connection.pinCode!])
|
|
||||||
let fileStr:String
|
|
||||||
if transfer.files.count==1{
|
|
||||||
fileStr=transfer.files[0].name
|
|
||||||
}else{
|
|
||||||
fileStr=String.localizedStringWithFormat(NSLocalizedString("NFiles", value: "%d files", comment: ""), transfer.files.count)
|
|
||||||
}
|
|
||||||
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": connection.id]
|
|
||||||
NDNotificationCenterHackery.removeDefaultAction(notificationContent)
|
|
||||||
let notificationReq=UNNotificationRequest(identifier: "transfer_"+connection.id, content: notificationContent, trigger: nil)
|
|
||||||
UNUserNotificationCenter.current().add(notificationReq)
|
|
||||||
}
|
|
||||||
|
|
||||||
func connectionWasTerminated(connection:InboundNearbyConnection, error:Error?){
|
|
||||||
activeConnections.removeValue(forKey: connection.id)
|
|
||||||
if let error=error{
|
|
||||||
let notificationContent=UNMutableNotificationContent()
|
|
||||||
notificationContent.title=String(format: NSLocalizedString("TransferError", value: "Failed to receive files from %@", comment: ""), arguments: [connection.remoteDeviceInfo!.name])
|
|
||||||
if let ne=(error as? NearbyError){
|
|
||||||
switch ne{
|
|
||||||
case .inputOutput(let er):
|
|
||||||
notificationContent.body=er.localizedDescription
|
|
||||||
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: "")
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
notificationContent.body=error.localizedDescription
|
|
||||||
}
|
|
||||||
notificationContent.categoryIdentifier="ERRORS"
|
|
||||||
UNUserNotificationCenter.current().add(UNNotificationRequest(identifier: "transferError_"+connection.id, content: notificationContent, trigger: nil))
|
|
||||||
}
|
|
||||||
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: ["transfer_"+connection.id])
|
|
||||||
}
|
|
||||||
|
|
||||||
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
|
|
||||||
activeConnections[response.notification.request.content.userInfo["transferID"]! as! String]?.submitUserConsent(accepted: response.actionIdentifier=="ACCEPT")
|
|
||||||
completionHandler()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Binary file not shown.
BIN
NearDrop/hu.lproj/Localizable.strings
Normal file
BIN
NearDrop/hu.lproj/Localizable.strings
Normal file
Binary file not shown.
22
NearDrop/hu.lproj/Localizable.stringsdict
Normal file
22
NearDrop/hu.lproj/Localizable.stringsdict
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?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>NFiles</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
|
<string>%#@files@</string>
|
||||||
|
<key>files</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringFormatSpecTypeKey</key>
|
||||||
|
<string>NSStringPluralRuleType</string>
|
||||||
|
<key>NSStringFormatValueTypeKey</key>
|
||||||
|
<string>d</string>
|
||||||
|
<key>one</key>
|
||||||
|
<string>%d fájl</string>
|
||||||
|
<key>other</key>
|
||||||
|
<string>%d fájl</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -28,4 +28,20 @@ extension Data{
|
|||||||
}
|
}
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func dataFromUrlSafeBase64(_ str:String)->Data?{
|
||||||
|
var regularB64=String(str.map{
|
||||||
|
if $0=="_"{
|
||||||
|
return "/"
|
||||||
|
}else if $0=="-"{
|
||||||
|
return "+"
|
||||||
|
}else{
|
||||||
|
return $0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
while (regularB64.count%4) != 0{
|
||||||
|
regularB64=regularB64+"="
|
||||||
|
}
|
||||||
|
return Data(base64Encoded: regularB64, options: .ignoreUnknownCharacters)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -63,8 +63,10 @@ class InboundNearbyConnection: NearbyConnection{
|
|||||||
}
|
}
|
||||||
}catch{
|
}catch{
|
||||||
lastError=error
|
lastError=error
|
||||||
print("Deserialization error: \(error)")
|
print("Deserialization error: \(error) in state \(currentState)")
|
||||||
|
#if !DEBUG
|
||||||
protocolError()
|
protocolError()
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,7 +281,7 @@ class InboundNearbyConnection: NearbyConnection{
|
|||||||
destinationURL: dest)
|
destinationURL: dest)
|
||||||
transferredFiles[file.payloadID]=info
|
transferredFiles[file.payloadID]=info
|
||||||
}
|
}
|
||||||
let metadata=TransferMetadata(files: transferredFiles.map({$0.value.meta}))
|
let metadata=TransferMetadata(files: transferredFiles.map({$0.value.meta}), id: id, pinCode: pinCode)
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.delegate?.obtainUserConsent(for: metadata, from: self.remoteDeviceInfo!, connection: self)
|
self.delegate?.obtainUserConsent(for: metadata, from: self.remoteDeviceInfo!, connection: self)
|
||||||
}
|
}
|
||||||
@@ -16,6 +16,7 @@ import BigInt
|
|||||||
|
|
||||||
class NearbyConnection{
|
class NearbyConnection{
|
||||||
internal static let SANE_FRAME_LENGTH=5*1024*1024
|
internal static let SANE_FRAME_LENGTH=5*1024*1024
|
||||||
|
private static let dispatchQueue=DispatchQueue(label: "me.grishka.NearDrop.queue", qos: .utility) // FIFO (non-concurrent) queue to avoid those exciting concurrency bugs
|
||||||
|
|
||||||
internal let connection:NWConnection
|
internal let connection:NWConnection
|
||||||
internal var remoteDeviceInfo:RemoteDeviceInfo?
|
internal var remoteDeviceInfo:RemoteDeviceInfo?
|
||||||
@@ -52,6 +53,7 @@ class NearbyConnection{
|
|||||||
func start(){
|
func start(){
|
||||||
connection.stateUpdateHandler={state in
|
connection.stateUpdateHandler={state in
|
||||||
if case .ready = state {
|
if case .ready = state {
|
||||||
|
self.connectionReady()
|
||||||
self.receiveFrameAsync()
|
self.receiveFrameAsync()
|
||||||
} else if case .failed(let err) = state {
|
} else if case .failed(let err) = state {
|
||||||
self.lastError=err
|
self.lastError=err
|
||||||
@@ -59,9 +61,12 @@ class NearbyConnection{
|
|||||||
self.handleConnectionClosure()
|
self.handleConnectionClosure()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
connection.start(queue: .global(qos: .utility))
|
//connection.start(queue: .global(qos: .utility))
|
||||||
|
connection.start(queue: NearbyConnection.dispatchQueue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func connectionReady(){}
|
||||||
|
|
||||||
internal func handleConnectionClosure(){
|
internal func handleConnectionClosure(){
|
||||||
print("Connection closed")
|
print("Connection closed")
|
||||||
}
|
}
|
||||||
@@ -132,22 +137,27 @@ class NearbyConnection{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func sendFrameAsync(_ frame:Data){
|
internal func sendFrameAsync(_ frame:Data, completion:(()->Void)?=nil){
|
||||||
|
if connectionClosed{
|
||||||
|
return
|
||||||
|
}
|
||||||
var lengthPrefixedData=Data(capacity: frame.count+4)
|
var lengthPrefixedData=Data(capacity: frame.count+4)
|
||||||
let length:Int=frame.count
|
let length:Int=frame.count
|
||||||
lengthPrefixedData.append(contentsOf: [
|
lengthPrefixedData.append(contentsOf: [
|
||||||
UInt8(length >> 24),
|
UInt8(truncatingIfNeeded: length >> 24),
|
||||||
UInt8(length >> 16),
|
UInt8(truncatingIfNeeded: length >> 16),
|
||||||
UInt8(length >> 8),
|
UInt8(truncatingIfNeeded: length >> 8),
|
||||||
UInt8(length)
|
UInt8(truncatingIfNeeded: length)
|
||||||
])
|
])
|
||||||
lengthPrefixedData.append(frame)
|
lengthPrefixedData.append(frame)
|
||||||
connection.send(content: lengthPrefixedData, completion: .contentProcessed({ error in
|
connection.send(content: lengthPrefixedData, completion: .contentProcessed({ error in
|
||||||
|
if let completion=completion{
|
||||||
|
completion()
|
||||||
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func encryptAndSendOfflineFrame(_ frame:Location_Nearby_Connections_OfflineFrame) throws{
|
internal func encryptAndSendOfflineFrame(_ frame:Location_Nearby_Connections_OfflineFrame, completion:(()->Void)?=nil) throws{
|
||||||
var d2dMsg=Securegcm_DeviceToDeviceMessage()
|
var d2dMsg=Securegcm_DeviceToDeviceMessage()
|
||||||
serverSeq+=1
|
serverSeq+=1
|
||||||
d2dMsg.sequenceNumber=serverSeq
|
d2dMsg.sequenceNumber=serverSeq
|
||||||
@@ -185,7 +195,7 @@ class NearbyConnection{
|
|||||||
var smsg=Securemessage_SecureMessage()
|
var smsg=Securemessage_SecureMessage()
|
||||||
smsg.headerAndBody=try hb.serializedData()
|
smsg.headerAndBody=try hb.serializedData()
|
||||||
smsg.signature=Data(HMAC<SHA256>.authenticationCode(for: smsg.headerAndBody, using: sendHmacKey!))
|
smsg.signature=Data(HMAC<SHA256>.authenticationCode(for: smsg.headerAndBody, using: sendHmacKey!))
|
||||||
sendFrameAsync(try smsg.serializedData())
|
sendFrameAsync(try smsg.serializedData(), completion: completion)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func sendTransferSetupFrame(_ frame:Sharing_Nearby_Frame) throws{
|
internal func sendTransferSetupFrame(_ frame:Sharing_Nearby_Frame) throws{
|
||||||
@@ -275,6 +285,9 @@ class NearbyConnection{
|
|||||||
try processFileChunk(frame: payloadTransfer)
|
try processFileChunk(frame: payloadTransfer)
|
||||||
}
|
}
|
||||||
}else if case .keepAlive = offlineFrame.v1.type{
|
}else if case .keepAlive = offlineFrame.v1.type{
|
||||||
|
#if DEBUG
|
||||||
|
print("Sent keep-alive")
|
||||||
|
#endif
|
||||||
sendKeepAlive(ack: true)
|
sendKeepAlive(ack: true)
|
||||||
}else{
|
}else{
|
||||||
print("Unhandled offline frame encrypted: \(offlineFrame)")
|
print("Unhandled offline frame encrypted: \(offlineFrame)")
|
||||||
@@ -402,51 +415,6 @@ class NearbyConnection{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum NearbyError:Error{
|
|
||||||
case protocolError(_ message:String)
|
|
||||||
case requiredFieldMissing
|
|
||||||
case ukey2
|
|
||||||
case inputOutput(cause:Errno)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct RemoteDeviceInfo{
|
|
||||||
let name:String
|
|
||||||
let type:DeviceType
|
|
||||||
|
|
||||||
enum DeviceType{
|
|
||||||
case unknown
|
|
||||||
case phone
|
|
||||||
case tablet
|
|
||||||
case computer
|
|
||||||
|
|
||||||
static func fromRawValue(value:Int) -> DeviceType{
|
|
||||||
switch value {
|
|
||||||
case 0:
|
|
||||||
return .unknown
|
|
||||||
case 1:
|
|
||||||
return .phone
|
|
||||||
case 2:
|
|
||||||
return .tablet
|
|
||||||
case 3:
|
|
||||||
return .computer
|
|
||||||
default:
|
|
||||||
return .unknown
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TransferMetadata{
|
|
||||||
let files:[FileMetadata]
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FileMetadata{
|
|
||||||
let name:String
|
|
||||||
let size:Int64
|
|
||||||
let mimeType:String
|
|
||||||
}
|
|
||||||
|
|
||||||
struct InternalFileInfo{
|
struct InternalFileInfo{
|
||||||
let meta:FileMetadata
|
let meta:FileMetadata
|
||||||
let payloadID:Int64
|
let payloadID:Int64
|
||||||
374
NearbyShare/NearbyConnectionManager.swift
Normal file
374
NearbyShare/NearbyConnectionManager.swift
Normal file
@@ -0,0 +1,374 @@
|
|||||||
|
//
|
||||||
|
// NearbyConnectionManager.swift
|
||||||
|
// NearDrop
|
||||||
|
//
|
||||||
|
// Created by Grishka on 08.04.2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Network
|
||||||
|
import System
|
||||||
|
|
||||||
|
public struct RemoteDeviceInfo{
|
||||||
|
public let name:String
|
||||||
|
public let type:DeviceType
|
||||||
|
public var id:String?
|
||||||
|
|
||||||
|
init(name: String, type: DeviceType, id: String? = nil) {
|
||||||
|
self.name = name
|
||||||
|
self.type = type
|
||||||
|
self.id = id
|
||||||
|
}
|
||||||
|
|
||||||
|
init(info:EndpointInfo){
|
||||||
|
self.name=info.name
|
||||||
|
self.type=info.deviceType
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum DeviceType:Int32{
|
||||||
|
case unknown=0
|
||||||
|
case phone
|
||||||
|
case tablet
|
||||||
|
case computer
|
||||||
|
|
||||||
|
public static func fromRawValue(value:Int) -> DeviceType{
|
||||||
|
switch value {
|
||||||
|
case 0:
|
||||||
|
return .unknown
|
||||||
|
case 1:
|
||||||
|
return .phone
|
||||||
|
case 2:
|
||||||
|
return .tablet
|
||||||
|
case 3:
|
||||||
|
return .computer
|
||||||
|
default:
|
||||||
|
return .unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public enum NearbyError:Error{
|
||||||
|
case protocolError(_ message:String)
|
||||||
|
case requiredFieldMissing
|
||||||
|
case ukey2
|
||||||
|
case inputOutput(cause:Errno)
|
||||||
|
case canceled(reason:CancellationReason)
|
||||||
|
|
||||||
|
public enum CancellationReason{
|
||||||
|
case userRejected, userCanceled, notEnoughSpace, unsupportedType, timedOut
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct TransferMetadata{
|
||||||
|
public let files:[FileMetadata]
|
||||||
|
public let id:String
|
||||||
|
public let pinCode:String?
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct FileMetadata{
|
||||||
|
public let name:String
|
||||||
|
public let size:Int64
|
||||||
|
public let mimeType:String
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FoundServiceInfo{
|
||||||
|
let service:NWBrowser.Result
|
||||||
|
var device:RemoteDeviceInfo?
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OutgoingTransferInfo{
|
||||||
|
let service:NWBrowser.Result
|
||||||
|
let device:RemoteDeviceInfo
|
||||||
|
let connection:OutboundNearbyConnection
|
||||||
|
let delegate:ShareExtensionDelegate
|
||||||
|
}
|
||||||
|
|
||||||
|
struct EndpointInfo{
|
||||||
|
let name:String
|
||||||
|
let deviceType:RemoteDeviceInfo.DeviceType
|
||||||
|
|
||||||
|
init(name: String, deviceType: RemoteDeviceInfo.DeviceType){
|
||||||
|
self.name = name
|
||||||
|
self.deviceType = deviceType
|
||||||
|
}
|
||||||
|
|
||||||
|
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=Int(data[0] & 7) >> 1
|
||||||
|
self.name=deviceName
|
||||||
|
self.deviceType=RemoteDeviceInfo.DeviceType.fromRawValue(value: rawDeviceType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func serialize()->Data{
|
||||||
|
// 1 byte: Version(3 bits)|Visibility(1 bit)|Device Type(3 bits)|Reserved(1 bits)
|
||||||
|
// Device types: unknown=0, phone=1, tablet=2, laptop=3
|
||||||
|
var endpointInfo:[UInt8]=[UInt8(deviceType.rawValue << 1)]
|
||||||
|
// 16 bytes: unknown random bytes
|
||||||
|
for _ in 0...15{
|
||||||
|
endpointInfo.append(UInt8.random(in: 0...255))
|
||||||
|
}
|
||||||
|
// Device name in UTF-8 prefixed with 1-byte length
|
||||||
|
var nameChars=[UInt8](name.utf8)
|
||||||
|
if nameChars.count>255{
|
||||||
|
nameChars=[UInt8](nameChars[0..<255])
|
||||||
|
}
|
||||||
|
endpointInfo.append(UInt8(nameChars.count))
|
||||||
|
for ch in nameChars{
|
||||||
|
endpointInfo.append(UInt8(ch))
|
||||||
|
}
|
||||||
|
return Data(endpointInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public protocol MainAppDelegate{
|
||||||
|
func obtainUserConsent(for transfer:TransferMetadata, from device:RemoteDeviceInfo)
|
||||||
|
func incomingTransfer(id:String, didFinishWith error:Error?)
|
||||||
|
}
|
||||||
|
|
||||||
|
public protocol ShareExtensionDelegate:AnyObject{
|
||||||
|
func addDevice(device:RemoteDeviceInfo)
|
||||||
|
func removeDevice(id:String)
|
||||||
|
func connectionWasEstablished(pinCode:String)
|
||||||
|
func connectionFailed(with error:Error)
|
||||||
|
func transferAccepted()
|
||||||
|
func transferProgress(progress:Double)
|
||||||
|
func transferFinished()
|
||||||
|
}
|
||||||
|
|
||||||
|
public class NearbyConnectionManager : NSObject, NetServiceDelegate, InboundNearbyConnectionDelegate, OutboundNearbyConnectionDelegate{
|
||||||
|
|
||||||
|
private var tcpListener:NWListener;
|
||||||
|
public let endpointID:[UInt8]=generateEndpointID()
|
||||||
|
private var mdnsService:NetService?
|
||||||
|
private var activeConnections:[String:InboundNearbyConnection]=[:]
|
||||||
|
private var foundServices:[String:FoundServiceInfo]=[:]
|
||||||
|
private var shareExtensionDelegates:[ShareExtensionDelegate]=[]
|
||||||
|
private var outgoingTransfers:[String:OutgoingTransferInfo]=[:]
|
||||||
|
public var mainAppDelegate:(any MainAppDelegate)?
|
||||||
|
private var discoveryRefCount=0
|
||||||
|
|
||||||
|
private var browser:NWBrowser?
|
||||||
|
|
||||||
|
public static let shared=NearbyConnectionManager()
|
||||||
|
|
||||||
|
override init() {
|
||||||
|
tcpListener=try! NWListener(using: NWParameters(tls: .none))
|
||||||
|
super.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func becomeVisible(){
|
||||||
|
startTCPListener()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func startTCPListener(){
|
||||||
|
tcpListener.stateUpdateHandler={(state:NWListener.State) in
|
||||||
|
if case .ready = state {
|
||||||
|
self.initMDNS()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tcpListener.newConnectionHandler={(connection:NWConnection) in
|
||||||
|
let id=UUID().uuidString
|
||||||
|
let conn=InboundNearbyConnection(connection: connection, id: id)
|
||||||
|
self.activeConnections[id]=conn
|
||||||
|
conn.delegate=self
|
||||||
|
conn.start()
|
||||||
|
}
|
||||||
|
tcpListener.start(queue: .global(qos: .utility))
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func generateEndpointID()->[UInt8]{
|
||||||
|
var id:[UInt8]=[]
|
||||||
|
let alphabet="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".compactMap {UInt8($0.asciiValue!)}
|
||||||
|
for _ in 0...3{
|
||||||
|
id.append(alphabet[Int.random(in: 0..<alphabet.count)])
|
||||||
|
}
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
private func initMDNS(){
|
||||||
|
let nameBytes:[UInt8]=[
|
||||||
|
0x23, // PCP
|
||||||
|
endpointID[0], endpointID[1], endpointID[2], endpointID[3],
|
||||||
|
0xFC, 0x9F, 0x5E, // Service ID hash
|
||||||
|
0, 0
|
||||||
|
]
|
||||||
|
let name=Data(nameBytes).urlSafeBase64EncodedString()
|
||||||
|
let endpointInfo=EndpointInfo(name: Host.current().localizedName!, deviceType: .computer)
|
||||||
|
|
||||||
|
let port:Int32=Int32(tcpListener.port!.rawValue)
|
||||||
|
mdnsService=NetService(domain: "", type: "_FC9F5ED42C8A._tcp.", name: name, port: port)
|
||||||
|
mdnsService?.delegate=self
|
||||||
|
mdnsService?.includesPeerToPeer=true
|
||||||
|
mdnsService?.setTXTRecord(NetService.data(fromTXTRecord: [
|
||||||
|
"n": endpointInfo.serialize().urlSafeBase64EncodedString().data(using: .utf8)!
|
||||||
|
]))
|
||||||
|
mdnsService?.publish()
|
||||||
|
}
|
||||||
|
|
||||||
|
func obtainUserConsent(for transfer: TransferMetadata, from device: RemoteDeviceInfo, connection: InboundNearbyConnection) {
|
||||||
|
guard let delegate=mainAppDelegate else {return}
|
||||||
|
delegate.obtainUserConsent(for: transfer, from: device)
|
||||||
|
}
|
||||||
|
|
||||||
|
func connectionWasTerminated(connection:InboundNearbyConnection, error:Error?){
|
||||||
|
guard let delegate=mainAppDelegate else {return}
|
||||||
|
delegate.incomingTransfer(id: connection.id, didFinishWith: error)
|
||||||
|
activeConnections.removeValue(forKey: connection.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func submitUserConsent(transferID:String, accept:Bool){
|
||||||
|
guard let conn=activeConnections[transferID] else {return}
|
||||||
|
conn.submitUserConsent(accepted: accept)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func startDeviceDiscovery(){
|
||||||
|
if discoveryRefCount==0{
|
||||||
|
foundServices.removeAll()
|
||||||
|
if browser==nil{
|
||||||
|
browser=NWBrowser(for: .bonjourWithTXTRecord(type: "_FC9F5ED42C8A._tcp.", domain: nil), using: .tcp)
|
||||||
|
browser?.browseResultsChangedHandler={newResults, changes in
|
||||||
|
for change in changes{
|
||||||
|
switch change{
|
||||||
|
case let .added(res):
|
||||||
|
self.maybeAddFoundDevice(service: res)
|
||||||
|
case let .removed(res):
|
||||||
|
self.maybeRemoveFoundDevice(service: res)
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
browser?.start(queue: .main)
|
||||||
|
}
|
||||||
|
discoveryRefCount+=1
|
||||||
|
}
|
||||||
|
|
||||||
|
public func stopDeviceDiscovery(){
|
||||||
|
discoveryRefCount-=1
|
||||||
|
assert(discoveryRefCount>=0)
|
||||||
|
if discoveryRefCount==0{
|
||||||
|
browser?.cancel()
|
||||||
|
browser=nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func addShareExtensionDelegate(_ delegate:ShareExtensionDelegate){
|
||||||
|
shareExtensionDelegates.append(delegate)
|
||||||
|
for service in foundServices.values{
|
||||||
|
guard let device=service.device else {continue}
|
||||||
|
delegate.addDevice(device: device)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func removeShareExtensionDelegate(_ delegate:ShareExtensionDelegate){
|
||||||
|
shareExtensionDelegates.removeAll(where: {$0===delegate})
|
||||||
|
}
|
||||||
|
|
||||||
|
public func cancelOutgoingTransfer(id:String){
|
||||||
|
guard let transfer=outgoingTransfers[id] else {return}
|
||||||
|
transfer.connection.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func endpointID(for service:NWBrowser.Result)->String?{
|
||||||
|
guard case let NWEndpoint.service(name: serviceName, type: _, domain: _, interface: _)=service.endpoint else {return nil}
|
||||||
|
guard let nameData=Data.dataFromUrlSafeBase64(serviceName) else {return nil}
|
||||||
|
guard nameData.count>=10 else {return nil}
|
||||||
|
let pcp=nameData[0]
|
||||||
|
guard pcp==0x23 else {return nil}
|
||||||
|
let endpointID=String(data: nameData.subdata(in: 1..<5), encoding: .ascii)!
|
||||||
|
let serviceIDHash=nameData.subdata(in: 5..<8)
|
||||||
|
guard serviceIDHash==Data([0xFC, 0x9F, 0x5E]) else {return nil}
|
||||||
|
return endpointID
|
||||||
|
}
|
||||||
|
|
||||||
|
private func maybeAddFoundDevice(service:NWBrowser.Result){
|
||||||
|
#if DEBUG
|
||||||
|
print("found service \(service)")
|
||||||
|
#endif
|
||||||
|
guard let endpointID=endpointID(for: service) else {return}
|
||||||
|
#if DEBUG
|
||||||
|
print("service name is valid, endpoint ID \(endpointID)")
|
||||||
|
#endif
|
||||||
|
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)
|
||||||
|
foundService.device=deviceInfo
|
||||||
|
foundServices[endpointID]=foundService
|
||||||
|
for delegate in shareExtensionDelegates{
|
||||||
|
delegate.addDevice(device: deviceInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func maybeRemoveFoundDevice(service:NWBrowser.Result){
|
||||||
|
guard let endpointID=endpointID(for: service) else {return}
|
||||||
|
guard let _=foundServices.removeValue(forKey: endpointID) else {return}
|
||||||
|
for delegate in shareExtensionDelegates {
|
||||||
|
delegate.removeDevice(id: endpointID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func startOutgoingTransfer(deviceID:String, delegate:ShareExtensionDelegate, urls:[URL]){
|
||||||
|
guard let info=foundServices[deviceID] else {return}
|
||||||
|
let tcp=NWProtocolTCP.Options.init()
|
||||||
|
tcp.noDelay=true
|
||||||
|
let nwconn=NWConnection(to: info.service.endpoint, using: NWParameters(tls: .none, tcp: tcp))
|
||||||
|
let conn=OutboundNearbyConnection(connection: nwconn, id: deviceID, urlsToSend: urls)
|
||||||
|
conn.delegate=self
|
||||||
|
let transfer=OutgoingTransferInfo(service: info.service, device: info.device!, connection: conn, delegate: delegate)
|
||||||
|
outgoingTransfers[deviceID]=transfer
|
||||||
|
conn.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
func outboundConnectionWasEstablished(connection: OutboundNearbyConnection) {
|
||||||
|
guard let transfer=outgoingTransfers[connection.id] else {return}
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
transfer.delegate.connectionWasEstablished(pinCode: connection.pinCode!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func outboundConnectionTransferAccepted(connection: OutboundNearbyConnection) {
|
||||||
|
guard let transfer=outgoingTransfers[connection.id] else {return}
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
transfer.delegate.transferAccepted()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func outboundConnection(connection: OutboundNearbyConnection, transferProgress: Double) {
|
||||||
|
guard let transfer=outgoingTransfers[connection.id] else {return}
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
transfer.delegate.transferProgress(progress: transferProgress)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func outboundConnection(connection: OutboundNearbyConnection, failedWithError: Error) {
|
||||||
|
guard let transfer=outgoingTransfers[connection.id] else {return}
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
transfer.delegate.connectionFailed(with: failedWithError)
|
||||||
|
}
|
||||||
|
outgoingTransfers.removeValue(forKey: connection.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func outboundConnectionTransferFinished(connection: OutboundNearbyConnection) {
|
||||||
|
guard let transfer=outgoingTransfers[connection.id] else {return}
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
transfer.delegate.transferFinished()
|
||||||
|
}
|
||||||
|
outgoingTransfers.removeValue(forKey: connection.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
424
NearbyShare/OutboundNearbyConnection.swift
Normal file
424
NearbyShare/OutboundNearbyConnection.swift
Normal file
@@ -0,0 +1,424 @@
|
|||||||
|
//
|
||||||
|
// OutboundNearbyConnection.swift
|
||||||
|
// NearbyShare
|
||||||
|
//
|
||||||
|
// Created by Grishka on 23.09.2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Network
|
||||||
|
import CryptoKit
|
||||||
|
import CommonCrypto
|
||||||
|
import System
|
||||||
|
import UniformTypeIdentifiers
|
||||||
|
|
||||||
|
import SwiftECC
|
||||||
|
import BigInt
|
||||||
|
|
||||||
|
class OutboundNearbyConnection:NearbyConnection{
|
||||||
|
private var currentState:State = .initial
|
||||||
|
private let urlsToSend:[URL]
|
||||||
|
private var ukeyClientFinishMsgData:Data?
|
||||||
|
private var queue:[OutgoingFileTransfer]=[]
|
||||||
|
private var currentTransfer:OutgoingFileTransfer?
|
||||||
|
public var delegate:OutboundNearbyConnectionDelegate?
|
||||||
|
private var totalBytesToSend:Int64=0
|
||||||
|
private var totalBytesSent:Int64=0
|
||||||
|
private var cancelled:Bool=false
|
||||||
|
|
||||||
|
enum State{
|
||||||
|
case initial, sentUkeyClientInit, sentUkeyClientFinish, sentPairedKeyEncryption, sentPairedKeyResult, sentIntroduction, sendingFiles
|
||||||
|
}
|
||||||
|
|
||||||
|
init(connection: NWConnection, id: String, urlsToSend:[URL]){
|
||||||
|
self.urlsToSend=urlsToSend
|
||||||
|
super.init(connection: connection, id: id)
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
if let transfer=currentTransfer, let handle=transfer.handle{
|
||||||
|
try? handle.close()
|
||||||
|
}
|
||||||
|
for transfer in queue{
|
||||||
|
if let handle=transfer.handle{
|
||||||
|
try? handle.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func cancel(){
|
||||||
|
cancelled=true
|
||||||
|
if encryptionDone{
|
||||||
|
var cancel=Sharing_Nearby_Frame()
|
||||||
|
cancel.version = .v1
|
||||||
|
cancel.v1=Sharing_Nearby_V1Frame()
|
||||||
|
cancel.v1.type = .cancel
|
||||||
|
try? sendTransferSetupFrame(cancel)
|
||||||
|
}
|
||||||
|
try? sendDisconnectionAndDisconnect()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func connectionReady() {
|
||||||
|
super.connectionReady()
|
||||||
|
do{
|
||||||
|
try sendConnectionRequest()
|
||||||
|
try sendUkey2ClientInit()
|
||||||
|
}catch{
|
||||||
|
lastError=error
|
||||||
|
protocolError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func isServer() -> Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override func processReceivedFrame(frameData: Data) {
|
||||||
|
do{
|
||||||
|
#if DEBUG
|
||||||
|
print("received \(frameData), state is \(currentState)")
|
||||||
|
#endif
|
||||||
|
switch currentState {
|
||||||
|
case .initial:
|
||||||
|
protocolError()
|
||||||
|
case .sentUkeyClientInit:
|
||||||
|
try processUkey2ServerInit(frame: try Securegcm_Ukey2Message(serializedData: frameData), raw: frameData)
|
||||||
|
case .sentUkeyClientFinish:
|
||||||
|
try processConnectionResponse(frame: try Location_Nearby_Connections_OfflineFrame(serializedData: frameData))
|
||||||
|
default:
|
||||||
|
let smsg=try Securemessage_SecureMessage(serializedData: frameData)
|
||||||
|
try decryptAndProcessReceivedSecureMessage(smsg)
|
||||||
|
}
|
||||||
|
}catch{
|
||||||
|
if case NearbyError.ukey2=error{
|
||||||
|
}else if currentState == .sentUkeyClientInit{
|
||||||
|
sendUkey2Alert(type: .badMessage)
|
||||||
|
}
|
||||||
|
lastError=error
|
||||||
|
protocolError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func processTransferSetupFrame(_ frame: Sharing_Nearby_Frame) throws {
|
||||||
|
if frame.hasV1 && frame.v1.hasType, case .cancel = frame.v1.type {
|
||||||
|
print("Transfer canceled")
|
||||||
|
try sendDisconnectionAndDisconnect()
|
||||||
|
delegate?.outboundConnection(connection: self, failedWithError: NearbyError.canceled(reason: .userCanceled))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
print(frame)
|
||||||
|
switch currentState{
|
||||||
|
case .sentPairedKeyEncryption:
|
||||||
|
try processPairedKeyEncryption(frame: frame)
|
||||||
|
case .sentPairedKeyResult:
|
||||||
|
try processPairedKeyResult(frame: frame)
|
||||||
|
case .sentIntroduction:
|
||||||
|
try processConsent(frame: frame)
|
||||||
|
case .sendingFiles:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
assertionFailure("Unexpected state \(currentState)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func protocolError() {
|
||||||
|
super.protocolError()
|
||||||
|
delegate?.outboundConnection(connection: self, failedWithError: lastError!)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func sendConnectionRequest() throws {
|
||||||
|
var frame=Location_Nearby_Connections_OfflineFrame()
|
||||||
|
frame.version = .v1
|
||||||
|
frame.v1=Location_Nearby_Connections_V1Frame()
|
||||||
|
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=Host.current().localizedName!
|
||||||
|
let endpointInfo=EndpointInfo(name: Host.current().localizedName!, deviceType: .computer)
|
||||||
|
frame.v1.connectionRequest.endpointInfo=endpointInfo.serialize()
|
||||||
|
frame.v1.connectionRequest.mediums=[.wifiLan]
|
||||||
|
sendFrameAsync(try frame.serializedData())
|
||||||
|
}
|
||||||
|
|
||||||
|
private func sendUkey2ClientInit() throws {
|
||||||
|
let domain=Domain.instance(curve: .EC256r1)
|
||||||
|
let (pubKey, privKey)=domain.makeKeyPair()
|
||||||
|
publicKey=pubKey
|
||||||
|
privateKey=privKey
|
||||||
|
|
||||||
|
var finishFrame=Securegcm_Ukey2Message()
|
||||||
|
finishFrame.messageType = .clientFinish
|
||||||
|
var finish=Securegcm_Ukey2ClientFinished()
|
||||||
|
var pkey=Securemessage_GenericPublicKey()
|
||||||
|
pkey.type = .ecP256
|
||||||
|
pkey.ecP256PublicKey=Securemessage_EcP256PublicKey()
|
||||||
|
pkey.ecP256PublicKey.x=Data(pubKey.w.x.asSignedBytes())
|
||||||
|
pkey.ecP256PublicKey.y=Data(pubKey.w.y.asSignedBytes())
|
||||||
|
finish.publicKey=try pkey.serializedData()
|
||||||
|
finishFrame.messageData=try finish.serializedData()
|
||||||
|
ukeyClientFinishMsgData=try finishFrame.serializedData()
|
||||||
|
|
||||||
|
var frame=Securegcm_Ukey2Message()
|
||||||
|
frame.messageType = .clientInit
|
||||||
|
|
||||||
|
var clientInit=Securegcm_Ukey2ClientInit()
|
||||||
|
clientInit.version=1
|
||||||
|
clientInit.random=Data.randomData(length: 32)
|
||||||
|
clientInit.nextProtocol="AES_256_CBC-HMAC_SHA256"
|
||||||
|
var sha=SHA512()
|
||||||
|
sha.update(data: ukeyClientFinishMsgData!)
|
||||||
|
var commitment=Securegcm_Ukey2ClientInit.CipherCommitment()
|
||||||
|
commitment.commitment=Data(sha.finalize())
|
||||||
|
commitment.handshakeCipher = .p256Sha512
|
||||||
|
clientInit.cipherCommitments.append(commitment)
|
||||||
|
frame.messageData=try clientInit.serializedData()
|
||||||
|
|
||||||
|
ukeyClientInitMsgData=try frame.serializedData()
|
||||||
|
sendFrameAsync(ukeyClientInitMsgData!)
|
||||||
|
currentState = .sentUkeyClientInit
|
||||||
|
}
|
||||||
|
|
||||||
|
private func processUkey2ServerInit(frame:Securegcm_Ukey2Message, raw:Data) throws{
|
||||||
|
ukeyServerInitMsgData=raw
|
||||||
|
guard frame.messageType == .serverInit else{
|
||||||
|
sendUkey2Alert(type: .badMessageType)
|
||||||
|
throw NearbyError.ukey2
|
||||||
|
}
|
||||||
|
let serverInit=try Securegcm_Ukey2ServerInit(serializedData: frame.messageData)
|
||||||
|
guard serverInit.version==1 else{
|
||||||
|
sendUkey2Alert(type: .badVersion)
|
||||||
|
throw NearbyError.ukey2
|
||||||
|
}
|
||||||
|
guard serverInit.random.count==32 else{
|
||||||
|
sendUkey2Alert(type: .badRandom)
|
||||||
|
throw NearbyError.ukey2
|
||||||
|
}
|
||||||
|
guard serverInit.handshakeCipher == .p256Sha512 else{
|
||||||
|
sendUkey2Alert(type: .badHandshakeCipher)
|
||||||
|
throw NearbyError.ukey2
|
||||||
|
}
|
||||||
|
|
||||||
|
let serverKey=try Securemessage_GenericPublicKey(serializedData: serverInit.publicKey)
|
||||||
|
try finalizeKeyExchange(peerKey: serverKey)
|
||||||
|
sendFrameAsync(ukeyClientFinishMsgData!)
|
||||||
|
currentState = .sentUkeyClientFinish
|
||||||
|
|
||||||
|
var resp=Location_Nearby_Connections_OfflineFrame()
|
||||||
|
resp.version = .v1
|
||||||
|
resp.v1=Location_Nearby_Connections_V1Frame()
|
||||||
|
resp.v1.type = .connectionResponse
|
||||||
|
resp.v1.connectionResponse=Location_Nearby_Connections_ConnectionResponseFrame()
|
||||||
|
resp.v1.connectionResponse.response = .accept
|
||||||
|
resp.v1.connectionResponse.status=0
|
||||||
|
resp.v1.connectionResponse.osInfo=Location_Nearby_Connections_OsInfo()
|
||||||
|
resp.v1.connectionResponse.osInfo.type = .apple
|
||||||
|
sendFrameAsync(try resp.serializedData())
|
||||||
|
|
||||||
|
encryptionDone=true
|
||||||
|
delegate?.outboundConnectionWasEstablished(connection: self)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func processConnectionResponse(frame:Location_Nearby_Connections_OfflineFrame) throws{
|
||||||
|
#if DEBUG
|
||||||
|
print("connection response: \(frame)")
|
||||||
|
#endif
|
||||||
|
guard frame.version == .v1 else {throw NearbyError.protocolError("Unexpected offline frame version \(frame.version)")}
|
||||||
|
guard frame.v1.type == .connectionResponse else {throw NearbyError.protocolError("Unexpected frame type \(frame.v1.type)")}
|
||||||
|
guard frame.v1.connectionResponse.response == .accept else {throw NearbyError.protocolError("Connection was rejected by recipient")}
|
||||||
|
|
||||||
|
var pairedEncryption=Sharing_Nearby_Frame()
|
||||||
|
pairedEncryption.version = .v1
|
||||||
|
pairedEncryption.v1=Sharing_Nearby_V1Frame()
|
||||||
|
pairedEncryption.v1.type = .pairedKeyEncryption
|
||||||
|
pairedEncryption.v1.pairedKeyEncryption=Sharing_Nearby_PairedKeyEncryptionFrame()
|
||||||
|
pairedEncryption.v1.pairedKeyEncryption.secretIDHash=Data.randomData(length: 6)
|
||||||
|
pairedEncryption.v1.pairedKeyEncryption.signedData=Data.randomData(length: 72)
|
||||||
|
try sendTransferSetupFrame(pairedEncryption)
|
||||||
|
|
||||||
|
currentState = .sentPairedKeyEncryption
|
||||||
|
}
|
||||||
|
|
||||||
|
private func processPairedKeyEncryption(frame:Sharing_Nearby_Frame) throws{
|
||||||
|
guard frame.hasV1, frame.v1.hasPairedKeyEncryption else { throw NearbyError.requiredFieldMissing }
|
||||||
|
var pairedResult=Sharing_Nearby_Frame()
|
||||||
|
pairedResult.version = .v1
|
||||||
|
pairedResult.v1=Sharing_Nearby_V1Frame()
|
||||||
|
pairedResult.v1.type = .pairedKeyResult
|
||||||
|
pairedResult.v1.pairedKeyResult=Sharing_Nearby_PairedKeyResultFrame()
|
||||||
|
pairedResult.v1.pairedKeyResult.status = .unable
|
||||||
|
try sendTransferSetupFrame(pairedResult)
|
||||||
|
currentState = .sentPairedKeyResult
|
||||||
|
}
|
||||||
|
|
||||||
|
private func processPairedKeyResult(frame:Sharing_Nearby_Frame) throws{
|
||||||
|
guard frame.hasV1, frame.v1.hasPairedKeyResult else { throw NearbyError.requiredFieldMissing }
|
||||||
|
|
||||||
|
var introduction=Sharing_Nearby_Frame()
|
||||||
|
introduction.version = .v1
|
||||||
|
introduction.v1.type = .introduction
|
||||||
|
if urlsToSend.count==1 && urlsToSend[0].scheme != "file"{
|
||||||
|
var meta=Sharing_Nearby_TextMetadata()
|
||||||
|
meta.type = .url
|
||||||
|
meta.textTitle=urlsToSend[0].host ?? "URL"
|
||||||
|
meta.size=Int64(urlsToSend[0].absoluteString.utf8.count)
|
||||||
|
meta.payloadID=Int64.random(in: Int64.min...Int64.max)
|
||||||
|
introduction.v1.introduction.textMetadata.append(meta)
|
||||||
|
}else{
|
||||||
|
for url in urlsToSend{
|
||||||
|
guard url.scheme=="file" else {continue}
|
||||||
|
var meta=Sharing_Nearby_FileMetadata()
|
||||||
|
meta.name=OutboundNearbyConnection.sanitizeFileName(name: url.lastPathComponent)
|
||||||
|
let attrs=try FileManager.default.attributesOfItem(atPath: url.path)
|
||||||
|
meta.size=(attrs[FileAttributeKey.size] as! NSNumber).int64Value
|
||||||
|
let typeID=try? url.resourceValues(forKeys: [.typeIdentifierKey]).typeIdentifier
|
||||||
|
meta.mimeType="application/octet-stream"
|
||||||
|
if let typeID=typeID{
|
||||||
|
let type=UTType(typeID)
|
||||||
|
if let type=type, let mimeType=type.preferredMIMEType{
|
||||||
|
meta.mimeType=mimeType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if meta.mimeType.starts(with: "image/"){
|
||||||
|
meta.type = .image
|
||||||
|
}else if meta.mimeType.starts(with: "video/"){
|
||||||
|
meta.type = .video
|
||||||
|
}else if(meta.mimeType.starts(with: "audio/")){
|
||||||
|
meta.type = .audio
|
||||||
|
}else if(url.pathExtension.lowercased()=="apk"){
|
||||||
|
meta.type = .app
|
||||||
|
}else{
|
||||||
|
meta.type = .unknown
|
||||||
|
}
|
||||||
|
meta.payloadID=Int64.random(in: Int64.min...Int64.max)
|
||||||
|
queue.append(OutgoingFileTransfer(url: url, payloadID: meta.payloadID, handle: try FileHandle(forReadingFrom: url), totalBytes: meta.size, currentOffset: 0))
|
||||||
|
introduction.v1.introduction.fileMetadata.append(meta)
|
||||||
|
totalBytesToSend+=meta.size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if DEBUG
|
||||||
|
print("sent introduction: \(introduction)")
|
||||||
|
#endif
|
||||||
|
try sendTransferSetupFrame(introduction)
|
||||||
|
|
||||||
|
currentState = .sentIntroduction
|
||||||
|
}
|
||||||
|
|
||||||
|
private func processConsent(frame:Sharing_Nearby_Frame) throws{
|
||||||
|
guard frame.version == .v1, frame.v1.type == .response else {throw NearbyError.requiredFieldMissing}
|
||||||
|
switch frame.v1.connectionResponse.status{
|
||||||
|
case .accept:
|
||||||
|
currentState = .sendingFiles
|
||||||
|
delegate?.outboundConnectionTransferAccepted(connection: self)
|
||||||
|
try sendNextFileChunk()
|
||||||
|
case .reject, .unknown:
|
||||||
|
delegate?.outboundConnection(connection: self, failedWithError: NearbyError.canceled(reason: .userRejected))
|
||||||
|
try sendDisconnectionAndDisconnect()
|
||||||
|
case .notEnoughSpace:
|
||||||
|
delegate?.outboundConnection(connection: self, failedWithError: NearbyError.canceled(reason: .notEnoughSpace))
|
||||||
|
try sendDisconnectionAndDisconnect()
|
||||||
|
case .timedOut:
|
||||||
|
delegate?.outboundConnection(connection: self, failedWithError: NearbyError.canceled(reason: .timedOut))
|
||||||
|
try sendDisconnectionAndDisconnect()
|
||||||
|
case .unsupportedAttachmentType:
|
||||||
|
delegate?.outboundConnection(connection: self, failedWithError: NearbyError.canceled(reason: .unsupportedType))
|
||||||
|
try sendDisconnectionAndDisconnect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func sendNextFileChunk() throws{
|
||||||
|
print("SEND NEXT: \(Thread.current)")
|
||||||
|
if cancelled{
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if currentTransfer==nil || currentTransfer?.currentOffset==currentTransfer?.totalBytes{
|
||||||
|
if currentTransfer != nil && currentTransfer?.handle != nil{
|
||||||
|
try currentTransfer?.handle?.close()
|
||||||
|
}
|
||||||
|
if queue.isEmpty{
|
||||||
|
#if DEBUG
|
||||||
|
print("Disconnecting because all files have been transferred")
|
||||||
|
#endif
|
||||||
|
try sendDisconnectionAndDisconnect()
|
||||||
|
delegate?.outboundConnectionTransferFinished(connection: self)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
currentTransfer=queue.removeFirst()
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let fileBuffer=try currentTransfer!.handle!.read(upToCount: 512*1024) else{
|
||||||
|
throw NearbyError.inputOutput(cause: Errno.ioError)
|
||||||
|
}
|
||||||
|
|
||||||
|
var transfer=Location_Nearby_Connections_PayloadTransferFrame()
|
||||||
|
transfer.packetType = .data
|
||||||
|
transfer.payloadChunk.offset=currentTransfer!.currentOffset
|
||||||
|
transfer.payloadChunk.flags=0
|
||||||
|
transfer.payloadChunk.body=fileBuffer
|
||||||
|
transfer.payloadHeader.id=currentTransfer!.payloadID
|
||||||
|
transfer.payloadHeader.type = .file
|
||||||
|
transfer.payloadHeader.totalSize=Int64(currentTransfer!.totalBytes)
|
||||||
|
transfer.payloadHeader.isSensitive=false
|
||||||
|
currentTransfer!.currentOffset+=Int64(fileBuffer.count)
|
||||||
|
|
||||||
|
var wrapper=Location_Nearby_Connections_OfflineFrame()
|
||||||
|
wrapper.version = .v1
|
||||||
|
wrapper.v1=Location_Nearby_Connections_V1Frame()
|
||||||
|
wrapper.v1.type = .payloadTransfer
|
||||||
|
wrapper.v1.payloadTransfer=transfer
|
||||||
|
try encryptAndSendOfflineFrame(wrapper, completion: {
|
||||||
|
do{
|
||||||
|
try self.sendNextFileChunk()
|
||||||
|
}catch{
|
||||||
|
self.lastError=error
|
||||||
|
self.protocolError()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
#if DEBUG
|
||||||
|
print("sent file chunk, current transfer: \(String(describing: currentTransfer))")
|
||||||
|
#endif
|
||||||
|
totalBytesSent+=Int64(fileBuffer.count)
|
||||||
|
delegate?.outboundConnection(connection: self, transferProgress: Double(totalBytesSent)/Double(totalBytesToSend))
|
||||||
|
|
||||||
|
if currentTransfer!.currentOffset==currentTransfer!.totalBytes{
|
||||||
|
// Signal end of file (yes, all this for one bit)
|
||||||
|
var transfer=Location_Nearby_Connections_PayloadTransferFrame()
|
||||||
|
transfer.packetType = .data
|
||||||
|
transfer.payloadChunk.offset=currentTransfer!.currentOffset
|
||||||
|
transfer.payloadChunk.flags=1 // <- this one here
|
||||||
|
transfer.payloadHeader.id=currentTransfer!.payloadID
|
||||||
|
transfer.payloadHeader.type = .file
|
||||||
|
transfer.payloadHeader.totalSize=Int64(currentTransfer!.totalBytes)
|
||||||
|
transfer.payloadHeader.isSensitive=false
|
||||||
|
|
||||||
|
var wrapper=Location_Nearby_Connections_OfflineFrame()
|
||||||
|
wrapper.version = .v1
|
||||||
|
wrapper.v1=Location_Nearby_Connections_V1Frame()
|
||||||
|
wrapper.v1.type = .payloadTransfer
|
||||||
|
wrapper.v1.payloadTransfer=transfer
|
||||||
|
try encryptAndSendOfflineFrame(wrapper)
|
||||||
|
#if DEBUG
|
||||||
|
print("sent EOF, current transfer: \(String(describing: currentTransfer))")
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func sanitizeFileName(name:String)->String{
|
||||||
|
return name.replacingOccurrences(of: "[\\/\\\\?%\\*:\\|\"<>=]", with: "_", options: .regularExpression)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate struct OutgoingFileTransfer{
|
||||||
|
let url:URL
|
||||||
|
let payloadID:Int64
|
||||||
|
let handle:FileHandle?
|
||||||
|
let totalBytes:Int64
|
||||||
|
var currentOffset:Int64
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol OutboundNearbyConnectionDelegate{
|
||||||
|
func outboundConnectionWasEstablished(connection:OutboundNearbyConnection)
|
||||||
|
func outboundConnection(connection:OutboundNearbyConnection, transferProgress:Double)
|
||||||
|
func outboundConnectionTransferAccepted(connection:OutboundNearbyConnection)
|
||||||
|
func outboundConnection(connection:OutboundNearbyConnection, failedWithError:Error)
|
||||||
|
func outboundConnectionTransferFinished(connection:OutboundNearbyConnection)
|
||||||
|
}
|
||||||
@@ -59,6 +59,7 @@ sequenceDiagram
|
|||||||
Server->>Client: UKEY2 ServerInit
|
Server->>Client: UKEY2 ServerInit
|
||||||
Client->>Server: UKEY2 ClientFinish
|
Client->>Server: UKEY2 ClientFinish
|
||||||
Server->>Client: Connection response
|
Server->>Client: Connection response
|
||||||
|
Client->>Server: Connection response
|
||||||
Note over Server, Client: All following packets are encrypted
|
Note over Server, Client: All following packets are encrypted
|
||||||
Server->>Client: Paired key encryption
|
Server->>Client: Paired key encryption
|
||||||
Client->>Server: Paired key encryption
|
Client->>Server: Paired key encryption
|
||||||
@@ -134,7 +135,7 @@ The **authentication string** is used for out-of-band key verification. Nearby S
|
|||||||
|
|
||||||
#### Connection response
|
#### Connection response
|
||||||
|
|
||||||
After the key exchange is complete, the server sends one last plaintext message to the client: a connection response. It's a subtype of offline frame saying that the server has accepted the connection. All the following communication is encrypted and wrapped in the payload layer.
|
After the key exchange is complete, the server and client send each other one last plaintext message: a connection response. It's a subtype of offline frame saying that the other party has accepted the connection. All the following communication is encrypted and wrapped in the payload layer.
|
||||||
|
|
||||||
### The encryption layer
|
### The encryption layer
|
||||||
|
|
||||||
|
|||||||
6
ShareExtension/Assets.xcassets/Contents.json
Normal file
6
ShareExtension/Assets.xcassets/Contents.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
ShareExtension/Assets.xcassets/NearDropIcon.imageset/16.png
vendored
Normal file
BIN
ShareExtension/Assets.xcassets/NearDropIcon.imageset/16.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1005 B |
BIN
ShareExtension/Assets.xcassets/NearDropIcon.imageset/32.png
vendored
Normal file
BIN
ShareExtension/Assets.xcassets/NearDropIcon.imageset/32.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
22
ShareExtension/Assets.xcassets/NearDropIcon.imageset/Contents.json
vendored
Normal file
22
ShareExtension/Assets.xcassets/NearDropIcon.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "16.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "32.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
ShareExtension/Base.lproj/Localizable.strings
Normal file
BIN
ShareExtension/Base.lproj/Localizable.strings
Normal file
Binary file not shown.
22
ShareExtension/Base.lproj/Localizable.stringsdict
Normal file
22
ShareExtension/Base.lproj/Localizable.stringsdict
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?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>NFiles</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
|
<string>%#@files@</string>
|
||||||
|
<key>files</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringFormatSpecTypeKey</key>
|
||||||
|
<string>NSStringPluralRuleType</string>
|
||||||
|
<key>NSStringFormatValueTypeKey</key>
|
||||||
|
<string>d</string>
|
||||||
|
<key>one</key>
|
||||||
|
<string>%d file</string>
|
||||||
|
<key>other</key>
|
||||||
|
<string>%d files</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
264
ShareExtension/Base.lproj/ShareViewController.xib
Normal file
264
ShareExtension/Base.lproj/ShareViewController.xib
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21701" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="macosx"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21701"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<objects>
|
||||||
|
<customObject id="-2" userLabel="File's Owner" customClass="ShareViewController" customModule="ShareExtension" customModuleProvider="target">
|
||||||
|
<connections>
|
||||||
|
<outlet property="contentWrap" destination="ACO-U2-AZj" id="PAE-Pw-Vyx"/>
|
||||||
|
<outlet property="filesIcon" destination="afM-Om-nLQ" id="I58-a3-SBZ"/>
|
||||||
|
<outlet property="filesLabel" destination="OEz-QK-nem" id="his-3f-6kx"/>
|
||||||
|
<outlet property="largeProgress" destination="fCj-wF-rEj" id="jEg-Uc-Ur7"/>
|
||||||
|
<outlet property="listView" destination="gZI-Zb-Blx" id="VOC-3j-Pwy"/>
|
||||||
|
<outlet property="listViewWrapper" destination="A4d-Qt-PsT" id="8g5-VW-MIb"/>
|
||||||
|
<outlet property="loadingOverlay" destination="fLZ-IA-bPS" id="84g-Ef-GzW"/>
|
||||||
|
<outlet property="progressDeviceIcon" destination="84U-lB-SRB" id="LEL-BY-2FY"/>
|
||||||
|
<outlet property="progressDeviceIconWrap" destination="jBg-Ih-Gac" id="69x-6y-z0c"/>
|
||||||
|
<outlet property="progressDeviceName" destination="dJc-gw-4ux" id="ZE4-Kx-5hv"/>
|
||||||
|
<outlet property="progressDeviceSecondaryIcon" destination="BEO-kU-a2Q" id="iks-QR-2Od"/>
|
||||||
|
<outlet property="progressProgressBar" destination="J5x-hu-Kn5" id="vYH-DN-03b"/>
|
||||||
|
<outlet property="progressState" destination="y8I-D3-scQ" id="Tj3-xa-8VG"/>
|
||||||
|
<outlet property="progressView" destination="Q9K-dc-THx" id="LlE-4d-mI5"/>
|
||||||
|
<outlet property="view" destination="1" id="2"/>
|
||||||
|
</connections>
|
||||||
|
</customObject>
|
||||||
|
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||||
|
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||||
|
<customView translatesAutoresizingMaskIntoConstraints="NO" id="1">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="404" height="248"/>
|
||||||
|
<subviews>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="NVE-vN-dkz">
|
||||||
|
<rect key="frame" x="325" y="3" width="76" height="32"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="60" id="cP1-hK-9ZX"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="6Up-t3-mwm">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<string key="keyEquivalent" base64-UTF8="YES">
|
||||||
|
Gw
|
||||||
|
</string>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="cancel:" target="-2" id="Qav-AK-DGt"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<stackView distribution="fill" orientation="horizontal" alignment="top" spacing="5" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="1xF-vr-5sH">
|
||||||
|
<rect key="frame" x="160" y="222" width="84" height="16"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="4M6-D5-WIf">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="16" height="16"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="16" id="BOe-aZ-Njc"/>
|
||||||
|
<constraint firstAttribute="height" constant="16" id="zLg-1a-wlZ"/>
|
||||||
|
</constraints>
|
||||||
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="NearDropIcon" id="q3u-Am-ZIA"/>
|
||||||
|
</imageView>
|
||||||
|
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="aNc-0i-CWK">
|
||||||
|
<rect key="frame" x="19" y="0.0" width="67" height="16"/>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" title="NearDrop" allowsEditingTextAttributes="YES" id="0xp-rC-2gr">
|
||||||
|
<font key="font" metaFont="systemBold"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<visibilityPriorities>
|
||||||
|
<integer value="1000"/>
|
||||||
|
<integer value="1000"/>
|
||||||
|
</visibilityPriorities>
|
||||||
|
<customSpacing>
|
||||||
|
<real value="3.4028234663852886e+38"/>
|
||||||
|
<real value="3.4028234663852886e+38"/>
|
||||||
|
</customSpacing>
|
||||||
|
</stackView>
|
||||||
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="afM-Om-nLQ">
|
||||||
|
<rect key="frame" x="10" y="196" width="16" height="16"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="16" id="3NE-mW-Jk0"/>
|
||||||
|
<constraint firstAttribute="height" constant="16" id="koV-ob-gSn"/>
|
||||||
|
</constraints>
|
||||||
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="jOq-9r-V1W"/>
|
||||||
|
</imageView>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="OEz-QK-nem">
|
||||||
|
<rect key="frame" x="29" y="196" width="367" height="16"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="truncatingMiddle" truncatesLastVisibleLine="YES" id="opy-Bj-x6u">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<customView translatesAutoresizingMaskIntoConstraints="NO" id="ACO-U2-AZj">
|
||||||
|
<rect key="frame" x="0.0" y="40" width="404" height="146"/>
|
||||||
|
</customView>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="OEz-QK-nem" firstAttribute="top" secondItem="1xF-vr-5sH" secondAttribute="bottom" constant="10" id="852-oh-vM0"/>
|
||||||
|
<constraint firstItem="OEz-QK-nem" firstAttribute="leading" secondItem="afM-Om-nLQ" secondAttribute="trailing" constant="5" id="CIj-Dk-Md0"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="ACO-U2-AZj" secondAttribute="trailing" id="HIg-sX-IK0"/>
|
||||||
|
<constraint firstItem="ACO-U2-AZj" firstAttribute="leading" secondItem="1" secondAttribute="leading" id="IGL-2g-wpe"/>
|
||||||
|
<constraint firstItem="afM-Om-nLQ" firstAttribute="leading" secondItem="1" secondAttribute="leading" constant="10" id="LK2-cg-qcY"/>
|
||||||
|
<constraint firstItem="NVE-vN-dkz" firstAttribute="top" secondItem="ACO-U2-AZj" secondAttribute="bottom" constant="10" id="RSD-PX-W0H"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="NVE-vN-dkz" secondAttribute="bottom" constant="10" id="USG-Gg-of3"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="NVE-vN-dkz" secondAttribute="trailing" constant="10" id="Z6U-4n-4tO"/>
|
||||||
|
<constraint firstItem="afM-Om-nLQ" firstAttribute="top" secondItem="1" secondAttribute="top" constant="36" id="fUG-kT-wb0"/>
|
||||||
|
<constraint firstItem="1xF-vr-5sH" firstAttribute="centerX" secondItem="1" secondAttribute="centerX" id="sCA-yf-S9L"/>
|
||||||
|
<constraint firstItem="1xF-vr-5sH" firstAttribute="top" secondItem="1" secondAttribute="top" constant="10" id="tFn-vk-9Cq"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="OEz-QK-nem" secondAttribute="trailing" constant="10" id="vyd-BI-oDH"/>
|
||||||
|
<constraint firstItem="ACO-U2-AZj" firstAttribute="top" secondItem="OEz-QK-nem" secondAttribute="bottom" constant="10" id="x5r-wc-s4x"/>
|
||||||
|
</constraints>
|
||||||
|
<point key="canvasLocation" x="140" y="-36"/>
|
||||||
|
</customView>
|
||||||
|
<stackView wantsLayer="YES" distribution="equalSpacing" orientation="vertical" alignment="centerX" spacing="0.0" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" id="fLZ-IA-bPS">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="521" height="100"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<progressIndicator maxValue="100" indeterminate="YES" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="fCj-wF-rEj">
|
||||||
|
<rect key="frame" x="245" y="68" width="32" height="32"/>
|
||||||
|
</progressIndicator>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="27P-yf-gUd">
|
||||||
|
<rect key="frame" x="189" y="42" width="144" height="16"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="Looking for devices..." id="NaJ-Wx-Pim">
|
||||||
|
<font key="font" textStyle="headline" name=".SFNS-Bold"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="V5W-Rm-YVM">
|
||||||
|
<rect key="frame" x="-2" y="0.0" width="525" height="32"/>
|
||||||
|
<textFieldCell key="cell" alignment="center" title="If you don't see your device, open "Google Files" app and tap "Receive" on the Nearby Share tab." id="vla-gF-eJo">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="27P-yf-gUd" firstAttribute="top" secondItem="fCj-wF-rEj" secondAttribute="bottom" constant="10" id="87W-ec-fMh"/>
|
||||||
|
<constraint firstItem="V5W-Rm-YVM" firstAttribute="top" secondItem="27P-yf-gUd" secondAttribute="bottom" constant="10" id="OSI-wI-LcY"/>
|
||||||
|
</constraints>
|
||||||
|
<visibilityPriorities>
|
||||||
|
<integer value="1000"/>
|
||||||
|
<integer value="1000"/>
|
||||||
|
<integer value="1000"/>
|
||||||
|
</visibilityPriorities>
|
||||||
|
<customSpacing>
|
||||||
|
<real value="3.4028234663852886e+38"/>
|
||||||
|
<real value="3.4028234663852886e+38"/>
|
||||||
|
<real value="3.4028234663852886e+38"/>
|
||||||
|
</customSpacing>
|
||||||
|
<point key="canvasLocation" x="660.5" y="-1.5"/>
|
||||||
|
</stackView>
|
||||||
|
<stackView wantsLayer="YES" distribution="equalSpacing" orientation="vertical" alignment="centerX" spacing="1" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" id="Q9K-dc-THx">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="384" height="130"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<customView translatesAutoresizingMaskIntoConstraints="NO" id="jBg-Ih-Gac">
|
||||||
|
<rect key="frame" x="168" y="82" width="48" height="48"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="84U-lB-SRB">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="48" height="48"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="48" id="9ld-k4-EkL"/>
|
||||||
|
<constraint firstAttribute="height" constant="48" id="XA4-2k-hqR"/>
|
||||||
|
</constraints>
|
||||||
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="rb3-78-PXe"/>
|
||||||
|
</imageView>
|
||||||
|
<imageView hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="BEO-kU-a2Q">
|
||||||
|
<rect key="frame" x="24" y="-8" width="32" height="32"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="32" id="05I-Ef-84Z"/>
|
||||||
|
<constraint firstAttribute="width" constant="32" id="V0g-Fz-HWH"/>
|
||||||
|
</constraints>
|
||||||
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="NSCaution" id="k1c-Xl-fTQ"/>
|
||||||
|
</imageView>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="BEO-kU-a2Q" secondAttribute="trailing" constant="-8" id="59U-Zi-ESa"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="BEO-kU-a2Q" secondAttribute="bottom" constant="-8" id="8ty-pq-miR"/>
|
||||||
|
<constraint firstItem="84U-lB-SRB" firstAttribute="top" secondItem="jBg-Ih-Gac" secondAttribute="top" id="MB9-UI-2mb"/>
|
||||||
|
<constraint firstItem="84U-lB-SRB" firstAttribute="leading" secondItem="jBg-Ih-Gac" secondAttribute="leading" id="b23-hJ-aWT"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="84U-lB-SRB" secondAttribute="trailing" id="eKl-3V-K49"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="84U-lB-SRB" secondAttribute="bottom" id="uh8-T1-Vd8"/>
|
||||||
|
</constraints>
|
||||||
|
</customView>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="dJc-gw-4ux">
|
||||||
|
<rect key="frame" x="-2" y="56" width="388" height="16"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="truncatingTail" alignment="center" id="QzI-qQ-OBb">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<progressIndicator maxValue="100" doubleValue="50" indeterminate="YES" style="bar" translatesAutoresizingMaskIntoConstraints="NO" id="J5x-hu-Kn5">
|
||||||
|
<rect key="frame" x="92" y="25" width="200" height="22"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="200" id="bM2-Ih-2UC"/>
|
||||||
|
</constraints>
|
||||||
|
</progressIndicator>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="y8I-D3-scQ">
|
||||||
|
<rect key="frame" x="165" y="0.0" width="54" height="16"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" id="Iin-UY-IE5">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="y8I-D3-scQ" firstAttribute="top" secondItem="J5x-hu-Kn5" secondAttribute="bottom" constant="10" id="4EB-xc-Qni"/>
|
||||||
|
<constraint firstItem="dJc-gw-4ux" firstAttribute="leading" secondItem="Q9K-dc-THx" secondAttribute="leading" id="IXP-am-A8h"/>
|
||||||
|
<constraint firstItem="J5x-hu-Kn5" firstAttribute="top" secondItem="dJc-gw-4ux" secondAttribute="bottom" constant="10" id="Zav-Ho-QEJ"/>
|
||||||
|
<constraint firstItem="dJc-gw-4ux" firstAttribute="top" secondItem="jBg-Ih-Gac" secondAttribute="bottom" constant="10" id="bIE-Es-ZbK"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="dJc-gw-4ux" secondAttribute="trailing" id="bKd-Vz-YA1"/>
|
||||||
|
</constraints>
|
||||||
|
<visibilityPriorities>
|
||||||
|
<integer value="1000"/>
|
||||||
|
<integer value="1000"/>
|
||||||
|
<integer value="1000"/>
|
||||||
|
<integer value="1000"/>
|
||||||
|
</visibilityPriorities>
|
||||||
|
<customSpacing>
|
||||||
|
<real value="3.4028234663852886e+38"/>
|
||||||
|
<real value="3.4028234663852886e+38"/>
|
||||||
|
<real value="3.4028234663852886e+38"/>
|
||||||
|
<real value="3.4028234663852886e+38"/>
|
||||||
|
</customSpacing>
|
||||||
|
<point key="canvasLocation" x="4" y="-288"/>
|
||||||
|
</stackView>
|
||||||
|
<scrollView wantsLayer="YES" autohidesScrollers="YES" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" id="A4d-Qt-PsT">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="271" height="172"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<clipView key="contentView" id="cCr-8P-Lya">
|
||||||
|
<rect key="frame" x="1" y="1" width="269" height="170"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<collectionView id="gZI-Zb-Blx">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="269" height="170"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES"/>
|
||||||
|
<collectionViewFlowLayout key="collectionViewLayout" minimumInteritemSpacing="10" minimumLineSpacing="10" id="ATS-Jd-odC">
|
||||||
|
<size key="itemSize" width="50" height="50"/>
|
||||||
|
</collectionViewFlowLayout>
|
||||||
|
<color key="primaryBackgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</collectionView>
|
||||||
|
</subviews>
|
||||||
|
</clipView>
|
||||||
|
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="3j2-z3-G1e">
|
||||||
|
<rect key="frame" x="-100" y="-100" width="402" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="wty-KV-JAg">
|
||||||
|
<rect key="frame" x="380" y="1" width="16" height="142"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
<point key="canvasLocation" x="-397.5" y="30"/>
|
||||||
|
</scrollView>
|
||||||
|
</objects>
|
||||||
|
<resources>
|
||||||
|
<image name="NSCaution" width="32" height="32"/>
|
||||||
|
<image name="NearDropIcon" width="16" height="16"/>
|
||||||
|
</resources>
|
||||||
|
</document>
|
||||||
26
ShareExtension/DeviceListCell.swift
Normal file
26
ShareExtension/DeviceListCell.swift
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
//
|
||||||
|
// DeviceListCell.swift
|
||||||
|
// ShareExtension
|
||||||
|
//
|
||||||
|
// Created by Grishka on 20.09.2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class DeviceListCell:NSCollectionViewItem {
|
||||||
|
public var clickHandler:(()->Void)?
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
let btn:NSButton=view as! NSButton
|
||||||
|
btn.isEnabled=true
|
||||||
|
btn.setButtonType(.momentaryPushIn)
|
||||||
|
btn.action=#selector(onClick)
|
||||||
|
btn.target=self
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func onClick(_ sender:Any?){
|
||||||
|
guard let handler=clickHandler else {return}
|
||||||
|
handler()
|
||||||
|
}
|
||||||
|
}
|
||||||
61
ShareExtension/DeviceListCell.xib
Normal file
61
ShareExtension/DeviceListCell.xib
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21701" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="macosx"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21701"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<objects>
|
||||||
|
<customObject id="-2" userLabel="File's Owner" customClass="DeviceListCell" customModule="ShareExtension" customModuleProvider="target">
|
||||||
|
<connections>
|
||||||
|
<outlet property="view" destination="Hz6-mo-xeY" id="gtq-ca-SBh"/>
|
||||||
|
</connections>
|
||||||
|
</customObject>
|
||||||
|
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||||
|
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||||
|
<customView misplaced="YES" id="Hz6-mo-xeY" customClass="NSButton">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="206" height="82"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="QPM-sE-maP">
|
||||||
|
<rect key="frame" x="-2" y="0.0" width="210" height="32"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="32" id="TBS-IE-2DT"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" truncatesLastVisibleLine="YES" alignment="center" title="Label" id="cXi-mf-qP0">
|
||||||
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="O1J-am-CoK">
|
||||||
|
<rect key="frame" x="79" y="42" width="48" height="48"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="48" id="KYr-2E-wCO"/>
|
||||||
|
<constraint firstAttribute="height" constant="48" id="nik-I6-YgB"/>
|
||||||
|
</constraints>
|
||||||
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="QGU-rR-rJQ"/>
|
||||||
|
</imageView>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="O1J-am-CoK" firstAttribute="centerX" secondItem="Hz6-mo-xeY" secondAttribute="centerX" id="R76-2L-pXb"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="QPM-sE-maP" secondAttribute="bottom" id="Ryn-nY-yZp"/>
|
||||||
|
<constraint firstItem="O1J-am-CoK" firstAttribute="top" secondItem="Hz6-mo-xeY" secondAttribute="top" id="ZFA-S9-fpT"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="QPM-sE-maP" secondAttribute="trailing" id="eOl-Va-HXq"/>
|
||||||
|
<constraint firstItem="QPM-sE-maP" firstAttribute="top" secondItem="O1J-am-CoK" secondAttribute="bottom" constant="10" id="leg-nD-mHB"/>
|
||||||
|
<constraint firstItem="QPM-sE-maP" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" id="vvH-Hq-iUk"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<action selector="onClick:" target="-2" id="mve-gp-q4i"/>
|
||||||
|
</connections>
|
||||||
|
<point key="canvasLocation" x="-60" y="150"/>
|
||||||
|
</customView>
|
||||||
|
<collectionViewItem id="1Lb-iJ-Nar" customClass="DeviceListCell" customModule="ShareExtension" customModuleProvider="target">
|
||||||
|
<connections>
|
||||||
|
<outlet property="imageView" destination="O1J-am-CoK" id="zQn-mV-bRy"/>
|
||||||
|
<outlet property="textField" destination="QPM-sE-maP" id="s0j-Qq-7iA"/>
|
||||||
|
<outlet property="view" destination="Hz6-mo-xeY" id="gZn-20-o77"/>
|
||||||
|
</connections>
|
||||||
|
</collectionViewItem>
|
||||||
|
</objects>
|
||||||
|
</document>
|
||||||
27
ShareExtension/Info.plist
Normal file
27
ShareExtension/Info.plist
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?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>NSExtension</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionAttributes</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionActivationRule</key>
|
||||||
|
<string>SUBQUERY (
|
||||||
|
extensionItems,
|
||||||
|
$extensionItem,
|
||||||
|
SUBQUERY (
|
||||||
|
$extensionItem.attachments,
|
||||||
|
$attachment,
|
||||||
|
ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.file-url"
|
||||||
|
AND NOT (ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.folder")
|
||||||
|
).@count == $extensionItem.attachments.@count
|
||||||
|
).@count > 0</string>
|
||||||
|
</dict>
|
||||||
|
<key>NSExtensionPointIdentifier</key>
|
||||||
|
<string>com.apple.share-services</string>
|
||||||
|
<key>NSExtensionPrincipalClass</key>
|
||||||
|
<string>$(PRODUCT_MODULE_NAME).ShareViewController</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
14
ShareExtension/ShareExtension.entitlements
Normal file
14
ShareExtension/ShareExtension.entitlements
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?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>com.apple.security.app-sandbox</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.files.user-selected.read-only</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.network.client</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.network.server</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
271
ShareExtension/ShareViewController.swift
Normal file
271
ShareExtension/ShareViewController.swift
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
//
|
||||||
|
// ShareViewController.swift
|
||||||
|
// ShareExtension
|
||||||
|
//
|
||||||
|
// Created by Grishka on 12.09.2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Cocoa
|
||||||
|
import NearbyShare
|
||||||
|
|
||||||
|
class ShareViewController: NSViewController, ShareExtensionDelegate{
|
||||||
|
|
||||||
|
private var urls:[URL]=[]
|
||||||
|
private var foundDevices:[RemoteDeviceInfo]=[]
|
||||||
|
private var chosenDevice:RemoteDeviceInfo?
|
||||||
|
private var lastError:Error?
|
||||||
|
|
||||||
|
@IBOutlet var filesIcon:NSImageView?
|
||||||
|
@IBOutlet var filesLabel:NSTextField?
|
||||||
|
@IBOutlet var loadingOverlay:NSStackView?
|
||||||
|
@IBOutlet var largeProgress:NSProgressIndicator?
|
||||||
|
@IBOutlet var listView:NSCollectionView?
|
||||||
|
@IBOutlet var listViewWrapper:NSView?
|
||||||
|
@IBOutlet var contentWrap:NSView?
|
||||||
|
@IBOutlet var progressView:NSView?
|
||||||
|
@IBOutlet var progressDeviceIcon:NSImageView?
|
||||||
|
@IBOutlet var progressDeviceName:NSTextField?
|
||||||
|
@IBOutlet var progressProgressBar:NSProgressIndicator?
|
||||||
|
@IBOutlet var progressState:NSTextField?
|
||||||
|
@IBOutlet var progressDeviceIconWrap:NSView?
|
||||||
|
@IBOutlet var progressDeviceSecondaryIcon:NSImageView?
|
||||||
|
|
||||||
|
override var nibName: NSNib.Name? {
|
||||||
|
return NSNib.Name("ShareViewController")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func loadView() {
|
||||||
|
super.loadView()
|
||||||
|
|
||||||
|
// Insert code here to customize the view
|
||||||
|
let item = self.extensionContext!.inputItems[0] as! NSExtensionItem
|
||||||
|
if let attachments = item.attachments {
|
||||||
|
for attachment in attachments as NSArray{
|
||||||
|
let provider=attachment as! NSItemProvider
|
||||||
|
provider.loadItem(forTypeIdentifier: kUTTypeURL as String) { data, err in
|
||||||
|
if let url=URL(dataRepresentation: data as! Data, relativeTo: nil, isAbsolute: false){
|
||||||
|
self.urls.append(url)
|
||||||
|
if self.urls.count==attachments.count{
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.urlsReady()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let cancelError = NSError(domain: NSCocoaErrorDomain, code: NSUserCancelledError, userInfo: nil)
|
||||||
|
self.extensionContext!.cancelRequest(withError: cancelError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
contentWrap!.addSubview(listViewWrapper!)
|
||||||
|
contentWrap!.addSubview(loadingOverlay!)
|
||||||
|
contentWrap!.addSubview(progressView!)
|
||||||
|
progressView!.isHidden=true
|
||||||
|
|
||||||
|
listViewWrapper!.translatesAutoresizingMaskIntoConstraints=false
|
||||||
|
loadingOverlay!.translatesAutoresizingMaskIntoConstraints=false
|
||||||
|
progressView!.translatesAutoresizingMaskIntoConstraints=false
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
NSLayoutConstraint(item: listViewWrapper!, attribute: .width, relatedBy: .equal, toItem: contentWrap, attribute: .width, multiplier: 1, constant: 0),
|
||||||
|
NSLayoutConstraint(item: listViewWrapper!, attribute: .height, relatedBy: .equal, toItem: contentWrap, attribute: .height, multiplier: 1, constant: 0),
|
||||||
|
|
||||||
|
NSLayoutConstraint(item: loadingOverlay!, attribute: .width, relatedBy: .equal, toItem: contentWrap, attribute: .width, multiplier: 1, constant: 0),
|
||||||
|
NSLayoutConstraint(item: loadingOverlay!, attribute: .centerY, relatedBy: .equal, toItem: contentWrap, attribute: .centerY, multiplier: 1, constant: 0),
|
||||||
|
|
||||||
|
NSLayoutConstraint(item: progressView!, attribute: .width, relatedBy: .equal, toItem: contentWrap, attribute: .width, multiplier: 1, constant: 0),
|
||||||
|
NSLayoutConstraint(item: progressView!, attribute: .centerY, relatedBy: .equal, toItem: contentWrap, attribute: .centerY, multiplier: 1, constant: 0)
|
||||||
|
])
|
||||||
|
|
||||||
|
largeProgress!.startAnimation(nil)
|
||||||
|
let flowLayout=NSCollectionViewFlowLayout()
|
||||||
|
flowLayout.itemSize=NSSize(width: 75, height: 90)
|
||||||
|
flowLayout.sectionInset=NSEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
|
||||||
|
flowLayout.minimumInteritemSpacing=10
|
||||||
|
flowLayout.minimumLineSpacing=10
|
||||||
|
listView!.collectionViewLayout=flowLayout
|
||||||
|
listView!.dataSource=self
|
||||||
|
|
||||||
|
progressDeviceIconWrap!.wantsLayer=true
|
||||||
|
progressDeviceIconWrap!.layer!.masksToBounds=false
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidLoad(){
|
||||||
|
super.viewDidLoad()
|
||||||
|
NearbyConnectionManager.shared.startDeviceDiscovery()
|
||||||
|
NearbyConnectionManager.shared.addShareExtensionDelegate(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewWillDisappear() {
|
||||||
|
if chosenDevice==nil{
|
||||||
|
NearbyConnectionManager.shared.stopDeviceDiscovery()
|
||||||
|
}
|
||||||
|
NearbyConnectionManager.shared.removeShareExtensionDelegate(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func cancel(_ sender: AnyObject?) {
|
||||||
|
if let device=chosenDevice{
|
||||||
|
NearbyConnectionManager.shared.cancelOutgoingTransfer(id: device.id!)
|
||||||
|
}
|
||||||
|
let cancelError = NSError(domain: NSCocoaErrorDomain, code: NSUserCancelledError, userInfo: nil)
|
||||||
|
self.extensionContext!.cancelRequest(withError: cancelError)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func urlsReady(){
|
||||||
|
for url in urls{
|
||||||
|
if url.isFileURL{
|
||||||
|
var isDirectory=UnsafeMutablePointer<ObjCBool>.allocate(capacity: 1)
|
||||||
|
if FileManager.default.fileExists(atPath: url.path, isDirectory: isDirectory) && isDirectory.pointee.boolValue{
|
||||||
|
print("Canceling share request because URL \(url) is a directory")
|
||||||
|
let cancelError = NSError(domain: NSCocoaErrorDomain, code: NSUserCancelledError, userInfo: nil)
|
||||||
|
self.extensionContext!.cancelRequest(withError: cancelError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if urls.count==1{
|
||||||
|
if urls[0].isFileURL{
|
||||||
|
filesLabel!.stringValue=urls[0].lastPathComponent
|
||||||
|
filesIcon!.image=NSWorkspace.shared.icon(forFile: urls[0].path)
|
||||||
|
}else if urls[0].scheme=="http" || urls[0].scheme=="https"{
|
||||||
|
filesLabel!.stringValue=urls[0].absoluteString
|
||||||
|
filesIcon!.image=NSImage(named: NSImage.networkName)
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
filesLabel!.stringValue=String.localizedStringWithFormat(NSLocalizedString("NFiles", value: "%d files", comment: ""), urls.count)
|
||||||
|
filesIcon!.image=NSImage(named: NSImage.multipleDocumentsName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addDevice(device: RemoteDeviceInfo) {
|
||||||
|
if foundDevices.isEmpty{
|
||||||
|
loadingOverlay?.animator().isHidden=true
|
||||||
|
}
|
||||||
|
foundDevices.append(device)
|
||||||
|
listView?.animator().insertItems(at: [[0, foundDevices.count-1]])
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeDevice(id: String){
|
||||||
|
if chosenDevice != nil{
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i in foundDevices.indices{
|
||||||
|
if foundDevices[i].id==id{
|
||||||
|
foundDevices.remove(at: i)
|
||||||
|
listView?.animator().deleteItems(at: [[0, i]])
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if foundDevices.isEmpty{
|
||||||
|
loadingOverlay?.animator().isHidden=false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func connectionWasEstablished(pinCode: String) {
|
||||||
|
progressState?.stringValue=String(format:NSLocalizedString("PinCode", value: "PIN: %@", comment: ""), arguments: [pinCode])
|
||||||
|
progressProgressBar?.isIndeterminate=false
|
||||||
|
progressProgressBar?.maxValue=1000
|
||||||
|
progressProgressBar?.doubleValue=0
|
||||||
|
}
|
||||||
|
|
||||||
|
func connectionFailed(with error: Error) {
|
||||||
|
progressProgressBar?.isIndeterminate=false
|
||||||
|
progressProgressBar?.maxValue=1000
|
||||||
|
progressProgressBar?.doubleValue=0
|
||||||
|
lastError=error
|
||||||
|
if let ne=(error as? NearbyError), case let .canceled(reason)=ne{
|
||||||
|
switch reason{
|
||||||
|
case .userRejected:
|
||||||
|
progressState?.stringValue=NSLocalizedString("TransferDeclined", value: "Declined", comment: "")
|
||||||
|
case .userCanceled:
|
||||||
|
progressState?.stringValue=NSLocalizedString("TransferCanceled", value: "Canceled", comment: "")
|
||||||
|
case .notEnoughSpace:
|
||||||
|
progressState?.stringValue=NSLocalizedString("NotEnoughSpace", value: "Not enough disk space", comment: "")
|
||||||
|
case .unsupportedType:
|
||||||
|
progressState?.stringValue=NSLocalizedString("UnsupportedType", value: "Attachment type not supported", comment: "")
|
||||||
|
case .timedOut:
|
||||||
|
progressState?.stringValue=NSLocalizedString("TransferTimedOut", value: "Timed out", comment: "")
|
||||||
|
}
|
||||||
|
progressDeviceSecondaryIcon?.isHidden=false
|
||||||
|
dismissDelayed()
|
||||||
|
}else{
|
||||||
|
let alert=NSAlert(error: error)
|
||||||
|
alert.beginSheetModal(for: view.window!) { resp in
|
||||||
|
self.extensionContext!.cancelRequest(withError: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func transferAccepted() {
|
||||||
|
progressState?.stringValue=NSLocalizedString("Sending", value: "Sending...", comment: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func transferProgress(progress: Double) {
|
||||||
|
progressProgressBar!.doubleValue=progress*progressProgressBar!.maxValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func transferFinished() {
|
||||||
|
progressState?.stringValue=NSLocalizedString("TransferFinished", value: "Transfer finished", comment: "")
|
||||||
|
dismissDelayed()
|
||||||
|
}
|
||||||
|
|
||||||
|
func selectDevice(device:RemoteDeviceInfo){
|
||||||
|
NearbyConnectionManager.shared.stopDeviceDiscovery()
|
||||||
|
listViewWrapper?.animator().isHidden=true
|
||||||
|
progressView?.animator().isHidden=false
|
||||||
|
progressDeviceName?.stringValue=device.name
|
||||||
|
progressDeviceIcon?.image=imageForDeviceType(type: device.type)
|
||||||
|
progressProgressBar?.startAnimation(nil)
|
||||||
|
progressState?.stringValue=NSLocalizedString("Connecting", value: "Connecting...", comment: "")
|
||||||
|
chosenDevice=device
|
||||||
|
NearbyConnectionManager.shared.startOutgoingTransfer(deviceID: device.id!, delegate: self, urls: urls)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func dismissDelayed(){
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now()+2.0){
|
||||||
|
if let error=self.lastError{
|
||||||
|
self.extensionContext!.cancelRequest(withError: error)
|
||||||
|
}else{
|
||||||
|
self.extensionContext!.completeRequest(returningItems: nil, completionHandler: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func imageForDeviceType(type:RemoteDeviceInfo.DeviceType)->NSImage{
|
||||||
|
let imageName:String
|
||||||
|
switch type{
|
||||||
|
case .tablet:
|
||||||
|
imageName="com.apple.ipad"
|
||||||
|
case .computer:
|
||||||
|
imageName="com.apple.macbookpro-13-unibody"
|
||||||
|
default: // also .phone
|
||||||
|
imageName="com.apple.iphone"
|
||||||
|
}
|
||||||
|
return NSImage(contentsOfFile: "/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/\(imageName).icns")!
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ShareViewController:NSCollectionViewDataSource{
|
||||||
|
func numberOfSections(in collectionView: NSCollectionView) -> Int {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||||
|
return foundDevices.count
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
|
||||||
|
let item=collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "DeviceListCell"), for: indexPath)
|
||||||
|
guard let collectionViewItem = item as? DeviceListCell else {return item}
|
||||||
|
let device=foundDevices[indexPath[1]]
|
||||||
|
collectionViewItem.textField?.stringValue=device.name
|
||||||
|
collectionViewItem.imageView?.image=imageForDeviceType(type: device.type)
|
||||||
|
// TODO maybe there's a better way to handle clicks on collection view items? I'm still new to Apple's way of doing UIs so I may do dumb shit occasionally
|
||||||
|
collectionViewItem.clickHandler={
|
||||||
|
self.selectDevice(device: device)
|
||||||
|
}
|
||||||
|
return collectionViewItem
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
ShareExtension/ru.lproj/Localizable.strings
Normal file
BIN
ShareExtension/ru.lproj/Localizable.strings
Normal file
Binary file not shown.
26
ShareExtension/ru.lproj/Localizable.stringsdict
Normal file
26
ShareExtension/ru.lproj/Localizable.stringsdict
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?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>NFiles</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
|
<string>%#@files@</string>
|
||||||
|
<key>files</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringFormatSpecTypeKey</key>
|
||||||
|
<string>NSStringPluralRuleType</string>
|
||||||
|
<key>NSStringFormatValueTypeKey</key>
|
||||||
|
<string>d</string>
|
||||||
|
<key>one</key>
|
||||||
|
<string>%d файл</string>
|
||||||
|
<key>few</key>
|
||||||
|
<string>%d файла</string>
|
||||||
|
<key>many</key>
|
||||||
|
<string>%d файлов</string>
|
||||||
|
<key>other</key>
|
||||||
|
<string>%d файлов</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
12
ShareExtension/ru.lproj/ShareViewController.strings
Normal file
12
ShareExtension/ru.lproj/ShareViewController.strings
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
/* Class = "NSTextFieldCell"; title = "NearDrop"; ObjectID = "0xp-rC-2gr"; */
|
||||||
|
"0xp-rC-2gr.title" = "NearDrop";
|
||||||
|
|
||||||
|
/* Class = "NSButtonCell"; title = "Cancel"; ObjectID = "6Up-t3-mwm"; */
|
||||||
|
"6Up-t3-mwm.title" = "Отменить";
|
||||||
|
|
||||||
|
/* Class = "NSTextFieldCell"; title = "Looking for devices..."; ObjectID = "NaJ-Wx-Pim"; */
|
||||||
|
"NaJ-Wx-Pim.title" = "Ищу устройства...";
|
||||||
|
|
||||||
|
/* Class = "NSTextFieldCell"; title = "If you don't see your device, open \"Files by Google\" app and tap \"Receive\" on the Nearby Share tab."; ObjectID = "vla-gF-eJo"; */
|
||||||
|
"vla-gF-eJo.title" = "Если Вы не видите своё устройство, откройте приложение \"Google Files\" и нажмите \"Получить\" на вкладке Обмен.";
|
||||||
Reference in New Issue
Block a user