First version with app notifications
This commit is contained in:
@@ -13,7 +13,14 @@
|
||||
540E6780242D2CF100871BBE /* VCRecordings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 540E677F242D2CF100871BBE /* VCRecordings.swift */; };
|
||||
540E67822433483D00871BBE /* VCEditRecording.swift in Sources */ = {isa = PBXBuildFile; fileRef = 540E67812433483D00871BBE /* VCEditRecording.swift */; };
|
||||
540E67842433FAFE00871BBE /* TVCPreviousRecords.swift in Sources */ = {isa = PBXBuildFile; fileRef = 540E67832433FAFE00871BBE /* TVCPreviousRecords.swift */; };
|
||||
541075CE24C9D43A00D6F1BF /* UNNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 541075CD24C9D43A00D6F1BF /* UNNotification.swift */; };
|
||||
541075CF24C9D43A00D6F1BF /* UNNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 541075CD24C9D43A00D6F1BF /* UNNotification.swift */; };
|
||||
541075D124CDBA0000D6F1BF /* ThrottledBatchQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 541075D024CDBA0000D6F1BF /* ThrottledBatchQueue.swift */; };
|
||||
541075D224CDBA0000D6F1BF /* ThrottledBatchQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 541075D024CDBA0000D6F1BF /* ThrottledBatchQueue.swift */; };
|
||||
5412F8EE24571B8200A63D7A /* VCDateFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5412F8ED24571B8100A63D7A /* VCDateFilter.swift */; };
|
||||
5412FCC224C628FA000DE429 /* TVCReminderAlerts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5412FCBF24C628F9000DE429 /* TVCReminderAlerts.swift */; };
|
||||
5412FCC324C628FA000DE429 /* TVCChooseAlertTone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5412FCC024C628F9000DE429 /* TVCChooseAlertTone.swift */; };
|
||||
5412FCC424C628FA000DE429 /* TVCConnectionAlerts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5412FCC124C628FA000DE429 /* TVCConnectionAlerts.swift */; };
|
||||
541A957623E602DF00C09C19 /* LaunchIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = 541A957523E602DF00C09C19 /* LaunchIcon.png */; };
|
||||
541AC5D82399498A00A769D7 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 541AC5D72399498A00A769D7 /* AppDelegate.swift */; };
|
||||
541AC5DD2399498A00A769D7 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 541AC5DB2399498A00A769D7 /* Main.storyboard */; };
|
||||
@@ -24,6 +31,30 @@
|
||||
541FC47824A1453F009154D8 /* VCCoOccurrence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 541FC47724A1453F009154D8 /* VCCoOccurrence.swift */; };
|
||||
541FC9872497D81C00962623 /* TheGreatDestroyer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 541FC9862497D81C00962623 /* TheGreatDestroyer.swift */; };
|
||||
542E2A982404973F001462DC /* TBCMain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 542E2A972404973F001462DC /* TBCMain.swift */; };
|
||||
543078AA24B5E12500278F2D /* snap2.caf in Resources */ = {isa = PBXBuildFile; fileRef = 5430789F24B5E12200278F2D /* snap2.caf */; };
|
||||
543078AB24B5E12500278F2D /* snap2.caf in Resources */ = {isa = PBXBuildFile; fileRef = 5430789F24B5E12200278F2D /* snap2.caf */; };
|
||||
543078AC24B5E12500278F2D /* typewriter2.caf in Resources */ = {isa = PBXBuildFile; fileRef = 543078A024B5E12200278F2D /* typewriter2.caf */; };
|
||||
543078AD24B5E12500278F2D /* typewriter2.caf in Resources */ = {isa = PBXBuildFile; fileRef = 543078A024B5E12200278F2D /* typewriter2.caf */; };
|
||||
543078AE24B5E12500278F2D /* wood1.caf in Resources */ = {isa = PBXBuildFile; fileRef = 543078A124B5E12300278F2D /* wood1.caf */; };
|
||||
543078AF24B5E12500278F2D /* wood1.caf in Resources */ = {isa = PBXBuildFile; fileRef = 543078A124B5E12300278F2D /* wood1.caf */; };
|
||||
543078B024B5E12500278F2D /* plop2.caf in Resources */ = {isa = PBXBuildFile; fileRef = 543078A224B5E12300278F2D /* plop2.caf */; };
|
||||
543078B124B5E12500278F2D /* plop2.caf in Resources */ = {isa = PBXBuildFile; fileRef = 543078A224B5E12300278F2D /* plop2.caf */; };
|
||||
543078B224B5E12500278F2D /* plop1.caf in Resources */ = {isa = PBXBuildFile; fileRef = 543078A324B5E12300278F2D /* plop1.caf */; };
|
||||
543078B324B5E12500278F2D /* plop1.caf in Resources */ = {isa = PBXBuildFile; fileRef = 543078A324B5E12300278F2D /* plop1.caf */; };
|
||||
543078B424B5E12500278F2D /* snap1.caf in Resources */ = {isa = PBXBuildFile; fileRef = 543078A424B5E12300278F2D /* snap1.caf */; };
|
||||
543078B524B5E12500278F2D /* snap1.caf in Resources */ = {isa = PBXBuildFile; fileRef = 543078A424B5E12300278F2D /* snap1.caf */; };
|
||||
543078B624B5E12500278F2D /* drum1.caf in Resources */ = {isa = PBXBuildFile; fileRef = 543078A524B5E12300278F2D /* drum1.caf */; };
|
||||
543078B724B5E12500278F2D /* drum1.caf in Resources */ = {isa = PBXBuildFile; fileRef = 543078A524B5E12300278F2D /* drum1.caf */; };
|
||||
543078B824B5E12500278F2D /* wood2.caf in Resources */ = {isa = PBXBuildFile; fileRef = 543078A624B5E12400278F2D /* wood2.caf */; };
|
||||
543078B924B5E12500278F2D /* wood2.caf in Resources */ = {isa = PBXBuildFile; fileRef = 543078A624B5E12400278F2D /* wood2.caf */; };
|
||||
543078BA24B5E12500278F2D /* typewriter1.caf in Resources */ = {isa = PBXBuildFile; fileRef = 543078A724B5E12400278F2D /* typewriter1.caf */; };
|
||||
543078BB24B5E12500278F2D /* typewriter1.caf in Resources */ = {isa = PBXBuildFile; fileRef = 543078A724B5E12400278F2D /* typewriter1.caf */; };
|
||||
543078BC24B5E12500278F2D /* clock.caf in Resources */ = {isa = PBXBuildFile; fileRef = 543078A824B5E12400278F2D /* clock.caf */; };
|
||||
543078BD24B5E12500278F2D /* clock.caf in Resources */ = {isa = PBXBuildFile; fileRef = 543078A824B5E12400278F2D /* clock.caf */; };
|
||||
543078BE24B5E12500278F2D /* drum2.caf in Resources */ = {isa = PBXBuildFile; fileRef = 543078A924B5E12500278F2D /* drum2.caf */; };
|
||||
543078BF24B5E12500278F2D /* drum2.caf in Resources */ = {isa = PBXBuildFile; fileRef = 543078A924B5E12500278F2D /* drum2.caf */; };
|
||||
543078C324B60F3B00278F2D /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 543078C124B60F3B00278F2D /* Settings.storyboard */; };
|
||||
543078C924B75CEA00278F2D /* PushNotificationAppOnly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 543078C824B75CD100278F2D /* PushNotificationAppOnly.swift */; };
|
||||
543CDB2023EEE61900B7F323 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 543CDB1F23EEE61900B7F323 /* PacketTunnelProvider.swift */; };
|
||||
543CDB2523EEE61900B7F323 /* GlassVPN.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 543CDB1D23EEE61900B7F323 /* GlassVPN.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
54448A2E2486464F00771C96 /* Array.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54448A2D2486464F00771C96 /* Array.swift */; };
|
||||
@@ -132,6 +163,8 @@
|
||||
54CA02BE2426D4F3003A5E04 /* DDLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54CA02BD2426D4F3003A5E04 /* DDLog.swift */; };
|
||||
54CA02C32426DCCD003A5E04 /* GCDAsyncSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 54CA02BF2426DCCC003A5E04 /* GCDAsyncSocket.m */; };
|
||||
54CA02C42426DCCD003A5E04 /* GCDAsyncUdpSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 54CA02C02426DCCD003A5E04 /* GCDAsyncUdpSocket.m */; };
|
||||
54CE8BC424B1ED2100CC1756 /* PushNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54CE8BC324B1ED2100CC1756 /* PushNotification.swift */; };
|
||||
54CE8BC524B1ED2100CC1756 /* PushNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54CE8BC324B1ED2100CC1756 /* PushNotification.swift */; };
|
||||
54D8B97A246C9F2000EB2414 /* FilterPipeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D8B979246C9F2000EB2414 /* FilterPipeline.swift */; };
|
||||
54D8B97C2471A7E000EB2414 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D8B97B2471A7E000EB2414 /* String.swift */; };
|
||||
54D8B97E2471B88900EB2414 /* DBCommon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D8B97D2471B88900EB2414 /* DBCommon.swift */; };
|
||||
@@ -181,7 +214,12 @@
|
||||
540E677F242D2CF100871BBE /* VCRecordings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VCRecordings.swift; sourceTree = "<group>"; };
|
||||
540E67812433483D00871BBE /* VCEditRecording.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VCEditRecording.swift; sourceTree = "<group>"; };
|
||||
540E67832433FAFE00871BBE /* TVCPreviousRecords.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TVCPreviousRecords.swift; sourceTree = "<group>"; };
|
||||
541075CD24C9D43A00D6F1BF /* UNNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UNNotification.swift; sourceTree = "<group>"; };
|
||||
541075D024CDBA0000D6F1BF /* ThrottledBatchQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThrottledBatchQueue.swift; sourceTree = "<group>"; };
|
||||
5412F8ED24571B8100A63D7A /* VCDateFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VCDateFilter.swift; sourceTree = "<group>"; };
|
||||
5412FCBF24C628F9000DE429 /* TVCReminderAlerts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TVCReminderAlerts.swift; sourceTree = "<group>"; };
|
||||
5412FCC024C628F9000DE429 /* TVCChooseAlertTone.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TVCChooseAlertTone.swift; sourceTree = "<group>"; };
|
||||
5412FCC124C628FA000DE429 /* TVCConnectionAlerts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TVCConnectionAlerts.swift; sourceTree = "<group>"; };
|
||||
541A957523E602DF00C09C19 /* LaunchIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = LaunchIcon.png; sourceTree = "<group>"; };
|
||||
541AC5D42399498A00A769D7 /* AppCheck.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AppCheck.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
541AC5D72399498A00A769D7 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
@@ -195,6 +233,19 @@
|
||||
541FC9862497D81C00962623 /* TheGreatDestroyer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TheGreatDestroyer.swift; sourceTree = "<group>"; };
|
||||
542E2A972404973F001462DC /* TBCMain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TBCMain.swift; sourceTree = "<group>"; };
|
||||
542E2A9924051556001462DC /* TVCSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TVCSettings.swift; sourceTree = "<group>"; };
|
||||
5430789F24B5E12200278F2D /* snap2.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = snap2.caf; sourceTree = "<group>"; };
|
||||
543078A024B5E12200278F2D /* typewriter2.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = typewriter2.caf; sourceTree = "<group>"; };
|
||||
543078A124B5E12300278F2D /* wood1.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = wood1.caf; sourceTree = "<group>"; };
|
||||
543078A224B5E12300278F2D /* plop2.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = plop2.caf; sourceTree = "<group>"; };
|
||||
543078A324B5E12300278F2D /* plop1.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = plop1.caf; sourceTree = "<group>"; };
|
||||
543078A424B5E12300278F2D /* snap1.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = snap1.caf; sourceTree = "<group>"; };
|
||||
543078A524B5E12300278F2D /* drum1.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = drum1.caf; sourceTree = "<group>"; };
|
||||
543078A624B5E12400278F2D /* wood2.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = wood2.caf; sourceTree = "<group>"; };
|
||||
543078A724B5E12400278F2D /* typewriter1.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = typewriter1.caf; sourceTree = "<group>"; };
|
||||
543078A824B5E12400278F2D /* clock.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = clock.caf; sourceTree = "<group>"; };
|
||||
543078A924B5E12500278F2D /* drum2.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = drum2.caf; sourceTree = "<group>"; };
|
||||
543078C224B60F3B00278F2D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Settings.storyboard; sourceTree = "<group>"; };
|
||||
543078C824B75CD100278F2D /* PushNotificationAppOnly.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushNotificationAppOnly.swift; sourceTree = "<group>"; };
|
||||
543CDB1D23EEE61900B7F323 /* GlassVPN.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = GlassVPN.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
543CDB1F23EEE61900B7F323 /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelProvider.swift; sourceTree = "<group>"; };
|
||||
543CDB2123EEE61900B7F323 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
@@ -307,6 +358,7 @@
|
||||
54CA02C02426DCCD003A5E04 /* GCDAsyncUdpSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDAsyncUdpSocket.m; sourceTree = "<group>"; };
|
||||
54CA02C12426DCCD003A5E04 /* GCDAsyncSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDAsyncSocket.h; sourceTree = "<group>"; };
|
||||
54CA02C22426DCCD003A5E04 /* GCDAsyncUdpSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDAsyncUdpSocket.h; sourceTree = "<group>"; };
|
||||
54CE8BC324B1ED2100CC1756 /* PushNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushNotification.swift; sourceTree = "<group>"; };
|
||||
54D8B979246C9F2000EB2414 /* FilterPipeline.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterPipeline.swift; sourceTree = "<group>"; };
|
||||
54D8B97B2471A7E000EB2414 /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = "<group>"; };
|
||||
54D8B97D2471B88900EB2414 /* DBCommon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DBCommon.swift; sourceTree = "<group>"; };
|
||||
@@ -360,6 +412,9 @@
|
||||
children = (
|
||||
542E2A9924051556001462DC /* TVCSettings.swift */,
|
||||
54B34593240E6343004C53CC /* TVCFilter.swift */,
|
||||
5412FCBF24C628F9000DE429 /* TVCReminderAlerts.swift */,
|
||||
5412FCC124C628FA000DE429 /* TVCConnectionAlerts.swift */,
|
||||
5412FCC024C628F9000DE429 /* TVCChooseAlertTone.swift */,
|
||||
);
|
||||
path = Settings;
|
||||
sourceTree = "<group>";
|
||||
@@ -411,6 +466,7 @@
|
||||
54B345B12422E029004C53CC /* unused */,
|
||||
541AC5E02399498B00A769D7 /* LaunchScreen.storyboard */,
|
||||
541AC5DB2399498A00A769D7 /* Main.storyboard */,
|
||||
543078C124B60F3B00278F2D /* Settings.storyboard */,
|
||||
541AC5DE2399498B00A769D7 /* Assets.xcassets */,
|
||||
541AC5E32399498B00A769D7 /* Info.plist */,
|
||||
54953E7023E473F10054345C /* Settings.bundle */,
|
||||
@@ -430,12 +486,31 @@
|
||||
542E2A9B24051F79001462DC /* media */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5430789E24B5E10E00278F2D /* sounds */,
|
||||
541A957523E602DF00C09C19 /* LaunchIcon.png */,
|
||||
54B345AF242264F8004C53CC /* third-level.txt */,
|
||||
);
|
||||
path = media;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5430789E24B5E10E00278F2D /* sounds */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
543078A824B5E12400278F2D /* clock.caf */,
|
||||
543078A524B5E12300278F2D /* drum1.caf */,
|
||||
543078A924B5E12500278F2D /* drum2.caf */,
|
||||
543078A324B5E12300278F2D /* plop1.caf */,
|
||||
543078A224B5E12300278F2D /* plop2.caf */,
|
||||
543078A424B5E12300278F2D /* snap1.caf */,
|
||||
5430789F24B5E12200278F2D /* snap2.caf */,
|
||||
543078A724B5E12400278F2D /* typewriter1.caf */,
|
||||
543078A024B5E12200278F2D /* typewriter2.caf */,
|
||||
543078A124B5E12300278F2D /* wood1.caf */,
|
||||
543078A624B5E12400278F2D /* wood2.caf */,
|
||||
);
|
||||
path = sounds;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
543CDB1E23EEE61900B7F323 /* GlassVPN */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -462,6 +537,9 @@
|
||||
549ECD9C24A7AD550097571C /* CustomAlert.swift */,
|
||||
541FC47524A12D01009154D8 /* IBViews.swift */,
|
||||
5404AEEC24A95F3F003B2F54 /* SlideInAnimation.swift */,
|
||||
541075D024CDBA0000D6F1BF /* ThrottledBatchQueue.swift */,
|
||||
54CE8BC324B1ED2100CC1756 /* PushNotification.swift */,
|
||||
543078C824B75CD100278F2D /* PushNotificationAppOnly.swift */,
|
||||
);
|
||||
path = "Common Classes";
|
||||
sourceTree = "<group>";
|
||||
@@ -484,6 +562,7 @@
|
||||
54B345A8241BBA0B004C53CC /* Logging.swift */,
|
||||
54E67E4E24A8E2910025D261 /* Equatable.swift */,
|
||||
54B345A5241BB982004C53CC /* Notifications.swift */,
|
||||
541075CD24C9D43A00D6F1BF /* UNNotification.swift */,
|
||||
54B345AA241BBA5B004C53CC /* AlertSheet.swift */,
|
||||
54E67E5024A8E8820025D261 /* View.swift */,
|
||||
541DCA6024A6B0F6005F1A4B /* Color.swift */,
|
||||
@@ -836,11 +915,23 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
543078AC24B5E12500278F2D /* typewriter2.caf in Resources */,
|
||||
54953E7123E473F10054345C /* Settings.bundle in Resources */,
|
||||
543078B024B5E12500278F2D /* plop2.caf in Resources */,
|
||||
541AC5E22399498B00A769D7 /* LaunchScreen.storyboard in Resources */,
|
||||
543078B824B5E12500278F2D /* wood2.caf in Resources */,
|
||||
543078BE24B5E12500278F2D /* drum2.caf in Resources */,
|
||||
543078B424B5E12500278F2D /* snap1.caf in Resources */,
|
||||
541AC5DF2399498B00A769D7 /* Assets.xcassets in Resources */,
|
||||
543078AE24B5E12500278F2D /* wood1.caf in Resources */,
|
||||
541AC5DD2399498A00A769D7 /* Main.storyboard in Resources */,
|
||||
54B345B0242264F8004C53CC /* third-level.txt in Resources */,
|
||||
543078BA24B5E12500278F2D /* typewriter1.caf in Resources */,
|
||||
543078B224B5E12500278F2D /* plop1.caf in Resources */,
|
||||
543078B624B5E12500278F2D /* drum1.caf in Resources */,
|
||||
543078BC24B5E12500278F2D /* clock.caf in Resources */,
|
||||
543078C324B60F3B00278F2D /* Settings.storyboard in Resources */,
|
||||
543078AA24B5E12500278F2D /* snap2.caf in Resources */,
|
||||
541A957623E602DF00C09C19 /* LaunchIcon.png in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -849,6 +940,17 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
543078AD24B5E12500278F2D /* typewriter2.caf in Resources */,
|
||||
543078BB24B5E12500278F2D /* typewriter1.caf in Resources */,
|
||||
543078BF24B5E12500278F2D /* drum2.caf in Resources */,
|
||||
543078AF24B5E12500278F2D /* wood1.caf in Resources */,
|
||||
543078B124B5E12500278F2D /* plop2.caf in Resources */,
|
||||
543078AB24B5E12500278F2D /* snap2.caf in Resources */,
|
||||
543078B924B5E12500278F2D /* wood2.caf in Resources */,
|
||||
543078B724B5E12500278F2D /* drum1.caf in Resources */,
|
||||
543078B524B5E12500278F2D /* snap1.caf in Resources */,
|
||||
543078B324B5E12500278F2D /* plop1.caf in Resources */,
|
||||
543078BD24B5E12500278F2D /* clock.caf in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -869,6 +971,7 @@
|
||||
54E540F2247C423200F7C34A /* DomainFilter.swift in Sources */,
|
||||
54D8B97E2471B88900EB2414 /* DBCommon.swift in Sources */,
|
||||
54D8B9832471BD8100EB2414 /* DBAppOnly.swift in Sources */,
|
||||
5412FCC224C628FA000DE429 /* TVCReminderAlerts.swift in Sources */,
|
||||
54E67E4D24A8E20D0025D261 /* TVCSettings.swift in Sources */,
|
||||
540E67842433FAFE00871BBE /* TVCPreviousRecords.swift in Sources */,
|
||||
54B345A6241BB982004C53CC /* Notifications.swift in Sources */,
|
||||
@@ -881,8 +984,10 @@
|
||||
54B345A9241BBA0B004C53CC /* Logging.swift in Sources */,
|
||||
54B34596240F0513004C53CC /* TableView.swift in Sources */,
|
||||
540E6780242D2CF100871BBE /* VCRecordings.swift in Sources */,
|
||||
541075D124CDBA0000D6F1BF /* ThrottledBatchQueue.swift in Sources */,
|
||||
54953E3323DC752E0054345C /* DBCore.swift in Sources */,
|
||||
54E67E4624A8B0FE0025D261 /* PrefsShared.swift in Sources */,
|
||||
5412FCC324C628FA000DE429 /* TVCChooseAlertTone.swift in Sources */,
|
||||
544F912024A67EC5001D4B00 /* TVCOccurrenceContext.swift in Sources */,
|
||||
54448A30248647D900771C96 /* Time.swift in Sources */,
|
||||
54953E6123E0D69A0054345C /* TVCHosts.swift in Sources */,
|
||||
@@ -890,6 +995,7 @@
|
||||
54953E5F23DEBE840054345C /* TVCDomains.swift in Sources */,
|
||||
54C056DB23E9E36E00214A3F /* AppInfoType.swift in Sources */,
|
||||
54D8B98624796E9900EB2414 /* GroupedDomainDataSource.swift in Sources */,
|
||||
541075CE24C9D43A00D6F1BF /* UNNotification.swift in Sources */,
|
||||
54953E6F23E44CD00054345C /* TVCHostDetails.swift in Sources */,
|
||||
54D8B97C2471A7E000EB2414 /* String.swift in Sources */,
|
||||
54E67E5124A8E8820025D261 /* View.swift in Sources */,
|
||||
@@ -899,6 +1005,8 @@
|
||||
5458EBC0243A3F2200CFEB15 /* TVCRecordingDetails.swift in Sources */,
|
||||
541FC9872497D81C00962623 /* TheGreatDestroyer.swift in Sources */,
|
||||
5412F8EE24571B8200A63D7A /* VCDateFilter.swift in Sources */,
|
||||
5412FCC424C628FA000DE429 /* TVCConnectionAlerts.swift in Sources */,
|
||||
543078C924B75CEA00278F2D /* PushNotificationAppOnly.swift in Sources */,
|
||||
545DDDD124436983003B6544 /* QuickUI.swift in Sources */,
|
||||
541AC5D82399498A00A769D7 /* AppDelegate.swift in Sources */,
|
||||
541FC47624A12D01009154D8 /* IBViews.swift in Sources */,
|
||||
@@ -907,6 +1015,7 @@
|
||||
54D8B97A246C9F2000EB2414 /* FilterPipeline.swift in Sources */,
|
||||
54B34594240E6343004C53CC /* TVCFilter.swift in Sources */,
|
||||
549ECD9D24A7AD550097571C /* CustomAlert.swift in Sources */,
|
||||
54CE8BC424B1ED2100CC1756 /* PushNotification.swift in Sources */,
|
||||
54E540FA2482414800F7C34A /* SyncUpdate.swift in Sources */,
|
||||
5404AEED24A95F3F003B2F54 /* SlideInAnimation.swift in Sources */,
|
||||
);
|
||||
@@ -950,6 +1059,7 @@
|
||||
54CA02A82426B2FD003A5E04 /* SOCKS5Adapter.swift in Sources */,
|
||||
54CA02792426B2FD003A5E04 /* Checksum.swift in Sources */,
|
||||
54CA02AD2426B2FD003A5E04 /* RejectAdapterFactory.swift in Sources */,
|
||||
541075D224CDBA0000D6F1BF /* ThrottledBatchQueue.swift in Sources */,
|
||||
54CA02672426B2FD003A5E04 /* RawTCPSocketProtocol.swift in Sources */,
|
||||
54CA02602426B2FD003A5E04 /* GCDProxyServer.swift in Sources */,
|
||||
54CA026B2426B2FD003A5E04 /* GCDTCPSocket.swift in Sources */,
|
||||
@@ -960,12 +1070,14 @@
|
||||
54CA02B02426B2FD003A5E04 /* SecureHTTPAdapterFactory.swift in Sources */,
|
||||
54CA02A62426B2FD003A5E04 /* AdapterSocket.swift in Sources */,
|
||||
54CA02742426B2FD003A5E04 /* IPMask.swift in Sources */,
|
||||
541075CF24C9D43A00D6F1BF /* UNNotification.swift in Sources */,
|
||||
54CA02BB2426B2FD003A5E04 /* SOCKS5ProxySocket.swift in Sources */,
|
||||
54D8B97F2471B89100EB2414 /* DBCommon.swift in Sources */,
|
||||
54CA02A42426B2FD003A5E04 /* SecureHTTPAdapter.swift in Sources */,
|
||||
54CA02942426B2FD003A5E04 /* DNSResolver.swift in Sources */,
|
||||
54CA025F2426B2FD003A5E04 /* ProxyServer.swift in Sources */,
|
||||
54CA02842426B2FD003A5E04 /* Rule.swift in Sources */,
|
||||
54CE8BC524B1ED2100CC1756 /* PushNotification.swift in Sources */,
|
||||
54CA02B92426B2FD003A5E04 /* DirectProxySocket.swift in Sources */,
|
||||
54751E522423955100168273 /* URL.swift in Sources */,
|
||||
54CA02A92426B2FD003A5E04 /* RejectAdapter.swift in Sources */,
|
||||
@@ -1031,6 +1143,14 @@
|
||||
name = LaunchScreen.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
543078C124B60F3B00278F2D /* Settings.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
543078C224B60F3B00278F2D /* Base */,
|
||||
);
|
||||
name = Settings.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
|
||||
@@ -1,51 +1,8 @@
|
||||
import NetworkExtension
|
||||
|
||||
fileprivate var filterDomains: [String]!
|
||||
fileprivate var filterOptions: [(block: Bool, ignore: Bool)]!
|
||||
|
||||
|
||||
// MARK: Backward DNS Binary Tree Lookup
|
||||
|
||||
fileprivate func reloadDomainFilter() {
|
||||
let tmp = AppDB?.loadFilters()?.map({
|
||||
(String($0.reversed()), $1)
|
||||
}).sorted(by: { $0.0 < $1.0 }) ?? []
|
||||
filterDomains = tmp.map { $0.0 }
|
||||
filterOptions = tmp.map { ($1.contains(.blocked), $1.contains(.ignored)) }
|
||||
}
|
||||
|
||||
fileprivate func filterIndex(for domain: String) -> Int {
|
||||
let reverseDomain = String(domain.reversed())
|
||||
var lo = 0, hi = filterDomains.count - 1
|
||||
while lo <= hi {
|
||||
let mid = (lo + hi)/2
|
||||
if filterDomains[mid] < reverseDomain {
|
||||
lo = mid + 1
|
||||
} else if reverseDomain < filterDomains[mid] {
|
||||
hi = mid - 1
|
||||
} else {
|
||||
return mid
|
||||
}
|
||||
}
|
||||
if lo > 0, reverseDomain.hasPrefix(filterDomains[lo - 1] + ".") {
|
||||
return lo - 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
import UserNotifications
|
||||
|
||||
private let queue = DispatchQueue.init(label: "PSIGlassDNSQueue", qos: .userInteractive, target: .main)
|
||||
|
||||
private func logAsync(_ domain: String, blocked: Bool) {
|
||||
queue.async {
|
||||
do {
|
||||
try AppDB?.logWrite(domain, blocked: blocked)
|
||||
} catch {
|
||||
DDLogWarn("Couldn't write: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: ObserverFactory
|
||||
|
||||
class LDObserverFactory: ObserverFactory {
|
||||
@@ -60,14 +17,10 @@ class LDObserverFactory: ObserverFactory {
|
||||
switch event {
|
||||
case .receivedRequest(let session, let socket):
|
||||
let i = filterIndex(for: session.host)
|
||||
if i >= 0 {
|
||||
let (block, ignore) = filterOptions[i]
|
||||
if !ignore { logAsync(session.host, blocked: block) }
|
||||
if block { socket.forceDisconnect() }
|
||||
} else {
|
||||
// TODO: disable filter during recordings
|
||||
logAsync(session.host, blocked: false)
|
||||
}
|
||||
let (block, ignore, cA, cB) = (i<0) ? (false, false, false, false) : filterOptions[i]
|
||||
let kill = ignore ? block : procRequest(session.host, blck: block, custA: cA, custB: cB)
|
||||
// TODO: disable ignore & block during recordings
|
||||
if kill { socket.forceDisconnect() }
|
||||
default:
|
||||
break
|
||||
}
|
||||
@@ -86,58 +39,35 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
|
||||
private var autoDeleteTimer: Timer? = nil
|
||||
|
||||
private func reloadSettings() {
|
||||
reloadDomainFilter()
|
||||
setAutoDelete(PrefsShared.AutoDeleteLogsDays)
|
||||
}
|
||||
// MARK: Delegate
|
||||
|
||||
override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) {
|
||||
DDLogVerbose("startTunnel with with options: \(String(describing: options))")
|
||||
PrefsShared.registerDefaults()
|
||||
do {
|
||||
try SQLiteDatabase.open().initCommonScheme()
|
||||
} catch {
|
||||
completionHandler(error)
|
||||
completionHandler(error) // if we cant open db, fail immediately
|
||||
return
|
||||
}
|
||||
reloadSettings()
|
||||
|
||||
if proxyServer != nil {
|
||||
proxyServer.stop()
|
||||
}
|
||||
// stop previous if any
|
||||
if proxyServer != nil { proxyServer.stop() }
|
||||
proxyServer = nil
|
||||
|
||||
// Create proxy
|
||||
let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: proxyServerAddress)
|
||||
settings.mtu = NSNumber(value: 1500)
|
||||
willInitProxy()
|
||||
|
||||
let proxySettings = NEProxySettings()
|
||||
proxySettings.httpEnabled = true;
|
||||
proxySettings.httpServer = NEProxyServer(address: proxyServerAddress, port: Int(proxyServerPort))
|
||||
proxySettings.httpsEnabled = true;
|
||||
proxySettings.httpsServer = NEProxyServer(address: proxyServerAddress, port: Int(proxyServerPort))
|
||||
proxySettings.excludeSimpleHostnames = false;
|
||||
proxySettings.exceptionList = []
|
||||
proxySettings.matchDomains = [""]
|
||||
|
||||
settings.dnsSettings = NEDNSSettings(servers: ["127.0.0.1"])
|
||||
settings.proxySettings = proxySettings;
|
||||
RawSocketFactory.TunnelProvider = self
|
||||
ObserverFactory.currentFactory = LDObserverFactory()
|
||||
|
||||
self.setTunnelNetworkSettings(settings) { error in
|
||||
self.setTunnelNetworkSettings(createProxy()) { error in
|
||||
guard error == nil else {
|
||||
DDLogError("setTunnelNetworkSettings error: \(String(describing: error))")
|
||||
DDLogError("setTunnelNetworkSettings error: \(error!)")
|
||||
completionHandler(error)
|
||||
return
|
||||
}
|
||||
completionHandler(nil)
|
||||
|
||||
self.proxyServer = GCDHTTPProxyServer(address: IPAddress(fromString: self.proxyServerAddress), port: Port(port: self.proxyServerPort))
|
||||
do {
|
||||
try self.proxyServer.start()
|
||||
self.didInitProxy()
|
||||
completionHandler(nil)
|
||||
}
|
||||
catch let proxyError {
|
||||
} catch let proxyError {
|
||||
DDLogError("Error starting proxy server \(proxyError)")
|
||||
completionHandler(proxyError)
|
||||
}
|
||||
@@ -146,15 +76,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
|
||||
override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
|
||||
DDLogVerbose("stopTunnel with reason: \(reason)")
|
||||
DNSServer.currentServer = nil
|
||||
RawSocketFactory.TunnelProvider = nil
|
||||
ObserverFactory.currentFactory = nil
|
||||
proxyServer.stop()
|
||||
proxyServer = nil
|
||||
filterDomains = nil
|
||||
filterOptions = nil
|
||||
autoDeleteTimer?.fire() // one last time before we quit
|
||||
autoDeleteTimer?.invalidate()
|
||||
shutdown()
|
||||
completionHandler()
|
||||
exit(EXIT_SUCCESS)
|
||||
}
|
||||
@@ -171,12 +93,117 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
case "auto-delete":
|
||||
setAutoDelete(Int(value) ?? PrefsShared.AutoDeleteLogsDays)
|
||||
return
|
||||
case "notify-prefs-change":
|
||||
reloadNotificationSettings()
|
||||
return
|
||||
default: break
|
||||
}
|
||||
}
|
||||
DDLogWarn("This should never happen! Received unknown handleAppMessage: \(message ?? messageData.base64EncodedString())")
|
||||
reloadSettings() // just in case we fallback to do everything
|
||||
}
|
||||
|
||||
// MARK: Helper
|
||||
|
||||
private func willInitProxy() {
|
||||
reloadSettings()
|
||||
}
|
||||
|
||||
private func createProxy() -> NEPacketTunnelNetworkSettings {
|
||||
let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: proxyServerAddress)
|
||||
settings.mtu = NSNumber(value: 1500)
|
||||
|
||||
let proxySettings = NEProxySettings()
|
||||
proxySettings.httpEnabled = true;
|
||||
proxySettings.httpServer = NEProxyServer(address: proxyServerAddress, port: Int(proxyServerPort))
|
||||
proxySettings.httpsEnabled = true;
|
||||
proxySettings.httpsServer = NEProxyServer(address: proxyServerAddress, port: Int(proxyServerPort))
|
||||
proxySettings.excludeSimpleHostnames = false;
|
||||
proxySettings.exceptionList = []
|
||||
proxySettings.matchDomains = [""]
|
||||
|
||||
settings.dnsSettings = NEDNSSettings(servers: ["127.0.0.1"])
|
||||
settings.proxySettings = proxySettings;
|
||||
RawSocketFactory.TunnelProvider = self
|
||||
ObserverFactory.currentFactory = LDObserverFactory()
|
||||
return settings
|
||||
}
|
||||
|
||||
private func didInitProxy() {
|
||||
if PrefsShared.RestartReminder.Enabled {
|
||||
PushNotification.scheduleRestartReminderBadge(on: false)
|
||||
}
|
||||
}
|
||||
|
||||
private func shutdown() {
|
||||
// proxy
|
||||
DNSServer.currentServer = nil
|
||||
RawSocketFactory.TunnelProvider = nil
|
||||
ObserverFactory.currentFactory = nil
|
||||
proxyServer.stop()
|
||||
proxyServer = nil
|
||||
// custom
|
||||
filterDomains = nil
|
||||
filterOptions = nil
|
||||
autoDeleteTimer?.fire() // one last time before we quit
|
||||
autoDeleteTimer?.invalidate()
|
||||
notifyTone = nil
|
||||
if PrefsShared.RestartReminder.Enabled {
|
||||
PushNotification.scheduleRestartReminderBadge(on: true)
|
||||
PushNotification.scheduleRestartReminderBanner()
|
||||
}
|
||||
}
|
||||
|
||||
private func reloadSettings() {
|
||||
reloadDomainFilter()
|
||||
setAutoDelete(PrefsShared.AutoDeleteLogsDays)
|
||||
reloadNotificationSettings()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ################################################################
|
||||
// #
|
||||
// # MARK: - Domain Filter
|
||||
// #
|
||||
// ################################################################
|
||||
|
||||
fileprivate var filterDomains: [String]!
|
||||
fileprivate var filterOptions: [(block: Bool, ignore: Bool, customA: Bool, customB: Bool)]!
|
||||
|
||||
extension PacketTunnelProvider {
|
||||
fileprivate func reloadDomainFilter() {
|
||||
let tmp = AppDB?.loadFilters()?.map({
|
||||
(String($0.reversed()), $1)
|
||||
}).sorted(by: { $0.0 < $1.0 }) ?? []
|
||||
let t1 = tmp.map { $0.0 }
|
||||
let t2 = tmp.map { ($1.contains(.blocked),
|
||||
$1.contains(.ignored),
|
||||
$1.contains(.customA),
|
||||
$1.contains(.customB)) }
|
||||
filterDomains = t1
|
||||
filterOptions = t2
|
||||
}
|
||||
}
|
||||
|
||||
/// Backward DNS Binary Tree Lookup
|
||||
fileprivate func filterIndex(for domain: String) -> Int {
|
||||
let reverseDomain = String(domain.reversed())
|
||||
var lo = 0, hi = filterDomains.count - 1
|
||||
while lo <= hi {
|
||||
let mid = (lo + hi)/2
|
||||
if filterDomains[mid] < reverseDomain {
|
||||
lo = mid + 1
|
||||
} else if reverseDomain < filterDomains[mid] {
|
||||
hi = mid - 1
|
||||
} else {
|
||||
return mid
|
||||
}
|
||||
}
|
||||
if lo > 0, reverseDomain.hasPrefix(filterDomains[lo - 1] + ".") {
|
||||
return lo - 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
|
||||
@@ -213,3 +240,58 @@ extension PacketTunnelProvider {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ################################################################
|
||||
// #
|
||||
// # MARK: - Notifications
|
||||
// #
|
||||
// ################################################################
|
||||
|
||||
fileprivate var notifyEnabled: Bool = false
|
||||
fileprivate var notifyIvertMode: Bool = false
|
||||
fileprivate var notifyListBlocked: Bool = false
|
||||
fileprivate var notifyListCustomA: Bool = false
|
||||
fileprivate var notifyListCustomB: Bool = false
|
||||
fileprivate var notifyListElse: Bool = false
|
||||
fileprivate var notifyTone: AnyObject?
|
||||
|
||||
extension PacketTunnelProvider {
|
||||
func reloadNotificationSettings() {
|
||||
notifyEnabled = PrefsShared.ConnectionAlerts.Enabled
|
||||
guard #available(iOS 10.0, *), notifyEnabled else {
|
||||
notifyTone = nil
|
||||
return
|
||||
}
|
||||
notifyIvertMode = PrefsShared.ConnectionAlerts.ExcludeMode
|
||||
notifyListBlocked = PrefsShared.ConnectionAlerts.Lists.Blocked
|
||||
notifyListCustomA = PrefsShared.ConnectionAlerts.Lists.CustomA
|
||||
notifyListCustomB = PrefsShared.ConnectionAlerts.Lists.CustomB
|
||||
notifyListElse = PrefsShared.ConnectionAlerts.Lists.Else
|
||||
notifyTone = UNNotificationSound.from(string: PrefsShared.ConnectionAlerts.Sound)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ################################################################
|
||||
// #
|
||||
// # MARK: - Process DNS Request
|
||||
// #
|
||||
// ################################################################
|
||||
|
||||
/// Log domain request and post notification if wanted.
|
||||
/// - Returns: `true` if the request shoud be blocked
|
||||
fileprivate func procRequest(_ domain: String, blck: Bool, custA: Bool, custB: Bool) -> Bool {
|
||||
queue.async {
|
||||
do { try AppDB?.logWrite(domain, blocked: blck) }
|
||||
catch { DDLogWarn("Couldn't write: \(error)") }
|
||||
}
|
||||
if #available(iOS 10.0, *), notifyEnabled {
|
||||
let onAnyList = notifyListBlocked && blck || notifyListCustomA && custA || notifyListCustomB && custB || notifyListElse
|
||||
if notifyIvertMode ? !onAnyList : onAnyList {
|
||||
// TODO: wait for response to block or allow connection
|
||||
PushNotification.scheduleConnectionAlert(domain, sound: notifyTone as! UNNotificationSound?)
|
||||
}
|
||||
}
|
||||
return blck
|
||||
}
|
||||
|
||||
@@ -15,6 +15,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
db.initAppOnlyScheme()
|
||||
}
|
||||
|
||||
Prefs.registerDefaults()
|
||||
PrefsShared.registerDefaults()
|
||||
|
||||
#if IOS_SIMULATOR
|
||||
TestDataSource.load()
|
||||
#endif
|
||||
|
||||
@@ -1130,7 +1130,7 @@ Duration: 60:00</string>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
<segue destination="qdB-ZO-LHY" kind="relationship" relationship="rootViewController" id="qJW-Jc-O4D"/>
|
||||
<segue destination="cV2-If-0fV" kind="relationship" relationship="rootViewController" id="vQq-KE-MOO"/>
|
||||
</connections>
|
||||
</navigationController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="bg9-bR-vlx" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
@@ -1138,274 +1138,17 @@ Duration: 60:00</string>
|
||||
<point key="canvasLocation" x="0.0" y="150"/>
|
||||
</scene>
|
||||
<!--Settings-->
|
||||
<scene sceneID="gEe-ny-NaU">
|
||||
<scene sceneID="l0f-fL-3tG">
|
||||
<objects>
|
||||
<tableViewController id="qdB-ZO-LHY" customClass="TVCSettings" customModule="AppCheck" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" separatorStyle="default" rowHeight="44" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="8kq-PY-wp7">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<sections>
|
||||
<tableViewSection headerTitle="VPN Proxy Settings" id="w58-6X-Jea">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="ghM-ze-fvp">
|
||||
<rect key="frame" x="0.0" y="55.5" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="ghM-ze-fvp" id="d2v-vz-QIB">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="kmY-ot-lJW">
|
||||
<rect key="frame" x="255" y="6.5" width="51" height="31"/>
|
||||
<connections>
|
||||
<action selector="toggleVPNProxy:" destination="qdB-ZO-LHY" eventType="valueChanged" id="y95-2Z-Uep"/>
|
||||
</connections>
|
||||
</switch>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="VPN Proxy enabled" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Qha-4I-go0">
|
||||
<rect key="frame" x="16" y="12" width="147" height="20"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="kmY-ot-lJW" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="Qha-4I-go0" secondAttribute="trailing" constant="8" id="Lnx-hC-xOx"/>
|
||||
<constraint firstItem="kmY-ot-lJW" firstAttribute="trailing" secondItem="d2v-vz-QIB" secondAttribute="trailingMargin" id="Ylz-D4-hz4"/>
|
||||
<constraint firstItem="Qha-4I-go0" firstAttribute="centerY" secondItem="d2v-vz-QIB" secondAttribute="centerY" id="dKE-By-qEu"/>
|
||||
<constraint firstItem="kmY-ot-lJW" firstAttribute="centerY" secondItem="Qha-4I-go0" secondAttribute="centerY" id="dgh-tx-Y8a"/>
|
||||
<constraint firstItem="Qha-4I-go0" firstAttribute="leading" secondItem="d2v-vz-QIB" secondAttribute="leadingMargin" id="rHx-0D-DPX"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="Logging Filter" id="EcH-KA-eLE">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="detailDisclosureButton" indentationWidth="10" reuseIdentifier="settingsIgnoredCell" textLabel="UdM-Zm-G9p" detailTextLabel="bHb-Tw-nPR" style="IBUITableViewCellStyleValue2" id="fZR-we-Y0k">
|
||||
<rect key="frame" x="0.0" y="155.5" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="fZR-we-Y0k" id="eqc-fj-p0d">
|
||||
<rect key="frame" x="0.0" y="0.0" width="261" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Ignore" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="UdM-Zm-G9p">
|
||||
<rect key="frame" x="16" y="13" width="91" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="0 Domains" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="bHb-Tw-nPR">
|
||||
<rect key="frame" x="113" y="13" width="73" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<segue destination="q3B-Yi-1bx" kind="push" identifier="segueFilterIgnored" id="EzT-Xq-wka"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="detailDisclosureButton" indentationWidth="10" reuseIdentifier="settingsBlockedCell" textLabel="fI0-Nt-Ucf" detailTextLabel="CGG-47-cdc" style="IBUITableViewCellStyleValue2" id="3pw-7c-M6R">
|
||||
<rect key="frame" x="0.0" y="199.5" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="3pw-7c-M6R" id="Smv-n1-917">
|
||||
<rect key="frame" x="0.0" y="0.0" width="261" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Block" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="fI0-Nt-Ucf">
|
||||
<rect key="frame" x="16" y="13" width="91" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="0 Domains" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="CGG-47-cdc">
|
||||
<rect key="frame" x="113" y="13" width="73" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<segue destination="q3B-Yi-1bx" kind="push" identifier="segueFilterBlocked" id="cOY-j0-75m"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="Privacy" id="wLR-T2-Qxm">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="8gD-At-D8n" detailTextLabel="Yy4-Ip-Wdv" style="IBUITableViewCellStyleValue1" id="Qyy-0U-yhd">
|
||||
<rect key="frame" x="0.0" y="299.5" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Qyy-0U-yhd" id="Mfs-fu-W5k">
|
||||
<rect key="frame" x="0.0" y="0.0" width="293" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Auto-delete logs" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="8gD-At-D8n">
|
||||
<rect key="frame" x="16" y="14" width="114" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Never" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Yy4-Ip-Wdv">
|
||||
<rect key="frame" x="239.5" y="12" width="45.5" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="Reset Settings" id="tBs-BI-JqN">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="Uii-Jp-53c">
|
||||
<rect key="frame" x="0.0" y="399.5" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Uii-Jp-53c" id="4Fp-Ox-yrk">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6B5-l4-Hgz">
|
||||
<rect key="frame" x="74.5" y="7" width="171" height="30"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
|
||||
<state key="normal" title="Reset Introduction Alerts"/>
|
||||
<connections>
|
||||
<action selector="resetTutorialAlerts:" destination="qdB-ZO-LHY" eventType="touchUpInside" id="hw8-as-4PZ"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="6B5-l4-Hgz" firstAttribute="centerY" secondItem="4Fp-Ox-yrk" secondAttribute="centerY" id="h2Y-P2-Feo"/>
|
||||
<constraint firstItem="6B5-l4-Hgz" firstAttribute="centerX" secondItem="4Fp-Ox-yrk" secondAttribute="centerX" id="jpA-gA-3jY"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="Xgc-6Z-IlH">
|
||||
<rect key="frame" x="0.0" y="443.5" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Xgc-6Z-IlH" id="efR-vn-6MX">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="sE3-Vh-0lM">
|
||||
<rect key="frame" x="111.5" y="7" width="97" height="30"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
|
||||
<state key="normal" title="Delete all logs">
|
||||
<color key="titleColor" systemColor="systemRedColor" red="1" green="0.23137254900000001" blue="0.18823529410000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="clearDatabaseResults" destination="qdB-ZO-LHY" eventType="touchUpInside" id="heU-m1-oJq"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="sE3-Vh-0lM" firstAttribute="centerX" secondItem="efR-vn-6MX" secondAttribute="centerX" id="TvC-jA-Wp5"/>
|
||||
<constraint firstItem="sE3-Vh-0lM" firstAttribute="centerY" secondItem="efR-vn-6MX" secondAttribute="centerY" id="WoM-cy-cAY"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="Advanced" id="Vlg-nm-VB3">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="VnR-9B-1zl">
|
||||
<rect key="frame" x="0.0" y="543.5" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="VnR-9B-1zl" id="ZTz-vZ-l5p">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="twS-Ne-dU0">
|
||||
<rect key="frame" x="125" y="7" width="70" height="30"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
|
||||
<state key="normal" title="Export DB"/>
|
||||
<connections>
|
||||
<action selector="exportDB" destination="qdB-ZO-LHY" eventType="touchUpInside" id="FYN-Zz-UK4"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="twS-Ne-dU0" firstAttribute="centerY" secondItem="ZTz-vZ-l5p" secondAttribute="centerY" id="LgK-8q-r6K"/>
|
||||
<constraint firstItem="twS-Ne-dU0" firstAttribute="centerX" secondItem="ZTz-vZ-l5p" secondAttribute="centerX" id="ltC-Ba-Bxr"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
</sections>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="qdB-ZO-LHY" id="RH3-xR-dpC"/>
|
||||
<outlet property="delegate" destination="qdB-ZO-LHY" id="eYf-Xd-2Jq"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" title="Settings" id="9Ce-p2-kGX"/>
|
||||
<connections>
|
||||
<outlet property="cellDomainsBlocked" destination="3pw-7c-M6R" id="AHT-FE-z0s"/>
|
||||
<outlet property="cellDomainsIgnored" destination="fZR-we-Y0k" id="Huy-N3-gz7"/>
|
||||
<outlet property="cellPrivacyAutoDelete" destination="Qyy-0U-yhd" id="PzN-iv-kFl"/>
|
||||
<outlet property="vpnToggle" destination="kmY-ot-lJW" id="yeS-DE-FfR"/>
|
||||
</connections>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="VNK-Z0-T0a" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
<viewControllerPlaceholder storyboardName="Settings" id="cV2-If-0fV" sceneMemberID="viewController">
|
||||
<navigationItem key="navigationItem" id="wlO-Ea-6he"/>
|
||||
</viewControllerPlaceholder>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="i3W-Ff-rJL" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="700" y="150"/>
|
||||
</scene>
|
||||
<!--Domains-->
|
||||
<scene sceneID="218-uP-X7b">
|
||||
<objects>
|
||||
<tableViewController id="q3B-Yi-1bx" customClass="TVCFilter" customModule="AppCheck" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" allowsSelection="NO" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="GSg-ZZ-F8J">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<prototypes>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="DomainFilterCell" textLabel="MrS-rb-RLB" style="IBUITableViewCellStyleDefault" id="EO2-ww-xuz">
|
||||
<rect key="frame" x="0.0" y="28" width="320" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="EO2-ww-xuz" id="AtR-ce-uYs">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.80000000000000004" id="MrS-rb-RLB">
|
||||
<rect key="frame" x="16" y="0.0" width="288" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</prototypes>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="q3B-Yi-1bx" id="eWw-VO-n1c"/>
|
||||
<outlet property="delegate" destination="q3B-Yi-1bx" id="02X-f0-d1a"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" title="Domains" id="FWA-IG-VIb">
|
||||
<barButtonItem key="rightBarButtonItem" systemItem="add" id="RFW-bp-wwH">
|
||||
<connections>
|
||||
<action selector="addNewFilter" destination="q3B-Yi-1bx" id="JID-eH-y0p"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="Xzo-dO-WpK" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1400" y="150"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<inferredMetricsTieBreakers>
|
||||
<segue reference="EzT-Xq-wka"/>
|
||||
<segue reference="ueN-6L-cP7"/>
|
||||
</inferredMetricsTieBreakers>
|
||||
<resources>
|
||||
|
||||
837
main/Base.lproj/Settings.storyboard
Normal file
837
main/Base.lproj/Settings.storyboard
Normal file
@@ -0,0 +1,837 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="qdB-ZO-LHY">
|
||||
<device id="retina4_0" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Settings-->
|
||||
<scene sceneID="gEe-ny-NaU">
|
||||
<objects>
|
||||
<tableViewController id="qdB-ZO-LHY" customClass="TVCSettings" customModule="AppCheck" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" separatorStyle="default" rowHeight="44" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="8kq-PY-wp7">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<sections>
|
||||
<tableViewSection headerTitle="VPN Proxy Settings" id="w58-6X-Jea">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" textLabel="3y8-eK-09n" style="IBUITableViewCellStyleDefault" id="ghM-ze-fvp">
|
||||
<rect key="frame" x="0.0" y="55.5" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="ghM-ze-fvp" id="d2v-vz-QIB">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="VPN Proxy Enabled" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="3y8-eK-09n">
|
||||
<rect key="frame" x="16" y="0.0" width="288" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ZAz-WT-FDb">
|
||||
<rect key="frame" x="257" y="6" width="49" height="31"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
|
||||
<connections>
|
||||
<action selector="toggleVPNProxy:" destination="qdB-ZO-LHY" eventType="valueChanged" id="DNS-71-2ga"/>
|
||||
</connections>
|
||||
</switch>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<outlet property="accessoryView" destination="ZAz-WT-FDb" id="SX3-lk-I3M"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="Logging Filter" id="EcH-KA-eLE">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="detailDisclosureButton" indentationWidth="10" reuseIdentifier="settingsIgnoredCell" textLabel="UdM-Zm-G9p" detailTextLabel="bHb-Tw-nPR" style="IBUITableViewCellStyleValue2" id="fZR-we-Y0k">
|
||||
<rect key="frame" x="0.0" y="155.5" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="fZR-we-Y0k" id="eqc-fj-p0d">
|
||||
<rect key="frame" x="0.0" y="0.0" width="261" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Ignore" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="UdM-Zm-G9p">
|
||||
<rect key="frame" x="16" y="13" width="91" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="0 Domains" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="bHb-Tw-nPR">
|
||||
<rect key="frame" x="113" y="13" width="73" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<segue destination="q3B-Yi-1bx" kind="push" identifier="segueFilterIgnored" id="EzT-Xq-wka"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="detailDisclosureButton" indentationWidth="10" reuseIdentifier="settingsBlockedCell" textLabel="fI0-Nt-Ucf" detailTextLabel="CGG-47-cdc" style="IBUITableViewCellStyleValue2" id="3pw-7c-M6R">
|
||||
<rect key="frame" x="0.0" y="199.5" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="3pw-7c-M6R" id="Smv-n1-917">
|
||||
<rect key="frame" x="0.0" y="0.0" width="261" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Block" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="fI0-Nt-Ucf">
|
||||
<rect key="frame" x="16" y="13" width="91" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="0 Domains" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="CGG-47-cdc">
|
||||
<rect key="frame" x="113" y="13" width="73" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<segue destination="q3B-Yi-1bx" kind="push" identifier="segueFilterBlocked" id="cOY-j0-75m"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="Notification Settings" id="gNL-sO-BEp">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" selectionStyle="blue" accessoryType="disclosureIndicator" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" textLabel="pN1-lL-bGz" detailTextLabel="ldE-NT-c2c" style="IBUITableViewCellStyleValue1" id="jZA-aP-aHG">
|
||||
<rect key="frame" x="0.0" y="299.5" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="jZA-aP-aHG" id="OYo-TE-SLp">
|
||||
<rect key="frame" x="0.0" y="0.0" width="293" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Reminders" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="pN1-lL-bGz">
|
||||
<rect key="frame" x="16" y="12" width="81.5" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Disabled" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="ldE-NT-c2c">
|
||||
<rect key="frame" x="218" y="12" width="67" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<segue destination="JYM-cs-i4H" kind="push" identifier="" id="uOT-Eo-Fm8"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" selectionStyle="blue" accessoryType="disclosureIndicator" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" textLabel="o7c-vQ-haI" detailTextLabel="VeV-go-DXR" style="IBUITableViewCellStyleValue1" id="OTC-Kt-LFT">
|
||||
<rect key="frame" x="0.0" y="343.5" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="OTC-Kt-LFT" id="RLb-Oi-WBg">
|
||||
<rect key="frame" x="0.0" y="0.0" width="293" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Connection Alerts" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="o7c-vQ-haI">
|
||||
<rect key="frame" x="16" y="12" width="137.5" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Disabled" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="VeV-go-DXR">
|
||||
<rect key="frame" x="218" y="12" width="67" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<segue destination="D2a-Po-vDU" kind="push" id="6NC-bN-nVR"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="Privacy Settings" id="wLR-T2-Qxm">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="8gD-At-D8n" detailTextLabel="Yy4-Ip-Wdv" style="IBUITableViewCellStyleValue1" id="Qyy-0U-yhd">
|
||||
<rect key="frame" x="0.0" y="443.5" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Qyy-0U-yhd" id="Mfs-fu-W5k">
|
||||
<rect key="frame" x="0.0" y="0.0" width="293" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Auto-Delete Logs" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="8gD-At-D8n">
|
||||
<rect key="frame" x="16" y="12" width="134" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Never" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Yy4-Ip-Wdv">
|
||||
<rect key="frame" x="239.5" y="12" width="45.5" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="Reset Settings" id="tBs-BI-JqN">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="Uii-Jp-53c">
|
||||
<rect key="frame" x="0.0" y="543.5" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Uii-Jp-53c" id="4Fp-Ox-yrk">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6B5-l4-Hgz">
|
||||
<rect key="frame" x="74.5" y="7" width="171" height="30"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
|
||||
<state key="normal" title="Reset Introduction Alerts"/>
|
||||
<connections>
|
||||
<action selector="resetTutorialAlerts:" destination="qdB-ZO-LHY" eventType="touchUpInside" id="hw8-as-4PZ"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="6B5-l4-Hgz" firstAttribute="centerY" secondItem="4Fp-Ox-yrk" secondAttribute="centerY" id="h2Y-P2-Feo"/>
|
||||
<constraint firstItem="6B5-l4-Hgz" firstAttribute="centerX" secondItem="4Fp-Ox-yrk" secondAttribute="centerX" id="jpA-gA-3jY"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="Xgc-6Z-IlH">
|
||||
<rect key="frame" x="0.0" y="587.5" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Xgc-6Z-IlH" id="efR-vn-6MX">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="sE3-Vh-0lM">
|
||||
<rect key="frame" x="111.5" y="7" width="97" height="30"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
|
||||
<state key="normal" title="Delete all logs">
|
||||
<color key="titleColor" systemColor="systemRedColor" red="1" green="0.23137254900000001" blue="0.18823529410000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="clearDatabaseResults" destination="qdB-ZO-LHY" eventType="touchUpInside" id="heU-m1-oJq"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="sE3-Vh-0lM" firstAttribute="centerX" secondItem="efR-vn-6MX" secondAttribute="centerX" id="TvC-jA-Wp5"/>
|
||||
<constraint firstItem="sE3-Vh-0lM" firstAttribute="centerY" secondItem="efR-vn-6MX" secondAttribute="centerY" id="WoM-cy-cAY"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="Advanced Settings" id="Vlg-nm-VB3">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="VnR-9B-1zl">
|
||||
<rect key="frame" x="0.0" y="687.5" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="VnR-9B-1zl" id="ZTz-vZ-l5p">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="twS-Ne-dU0">
|
||||
<rect key="frame" x="125" y="7" width="70" height="30"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
|
||||
<state key="normal" title="Export DB"/>
|
||||
<connections>
|
||||
<action selector="exportDB" destination="qdB-ZO-LHY" eventType="touchUpInside" id="FYN-Zz-UK4"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="twS-Ne-dU0" firstAttribute="centerY" secondItem="ZTz-vZ-l5p" secondAttribute="centerY" id="LgK-8q-r6K"/>
|
||||
<constraint firstItem="twS-Ne-dU0" firstAttribute="centerX" secondItem="ZTz-vZ-l5p" secondAttribute="centerX" id="ltC-Ba-Bxr"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
</sections>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="qdB-ZO-LHY" id="RH3-xR-dpC"/>
|
||||
<outlet property="delegate" destination="qdB-ZO-LHY" id="eYf-Xd-2Jq"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" title="Settings" id="9Ce-p2-kGX"/>
|
||||
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" prompted="NO"/>
|
||||
<simulatedTabBarMetrics key="simulatedBottomBarMetrics"/>
|
||||
<connections>
|
||||
<outlet property="cellDomainsBlocked" destination="3pw-7c-M6R" id="AHT-FE-z0s"/>
|
||||
<outlet property="cellDomainsIgnored" destination="fZR-we-Y0k" id="Huy-N3-gz7"/>
|
||||
<outlet property="cellNotificationConnectionAlert" destination="OTC-Kt-LFT" id="XiG-CC-4lC"/>
|
||||
<outlet property="cellNotificationReminder" destination="jZA-aP-aHG" id="sjo-2s-rqW"/>
|
||||
<outlet property="cellPrivacyAutoDelete" destination="Qyy-0U-yhd" id="PzN-iv-kFl"/>
|
||||
<outlet property="vpnToggle" destination="ZAz-WT-FDb" id="lGX-J8-WrU"/>
|
||||
</connections>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="VNK-Z0-T0a" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-200" y="0.0"/>
|
||||
</scene>
|
||||
<!--Domains-->
|
||||
<scene sceneID="218-uP-X7b">
|
||||
<objects>
|
||||
<tableViewController id="q3B-Yi-1bx" customClass="TVCFilter" customModule="AppCheck" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" allowsSelection="NO" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="GSg-ZZ-F8J">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<prototypes>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="DomainFilterCell" textLabel="MrS-rb-RLB" style="IBUITableViewCellStyleDefault" id="EO2-ww-xuz">
|
||||
<rect key="frame" x="0.0" y="28" width="320" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="EO2-ww-xuz" id="AtR-ce-uYs">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.80000000000000004" id="MrS-rb-RLB">
|
||||
<rect key="frame" x="16" y="0.0" width="288" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</prototypes>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="q3B-Yi-1bx" id="eWw-VO-n1c"/>
|
||||
<outlet property="delegate" destination="q3B-Yi-1bx" id="02X-f0-d1a"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" title="Domains" id="FWA-IG-VIb">
|
||||
<barButtonItem key="rightBarButtonItem" systemItem="add" id="RFW-bp-wwH">
|
||||
<connections>
|
||||
<action selector="addNewFilter" destination="q3B-Yi-1bx" id="JID-eH-y0p"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="Xzo-dO-WpK" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1500" y="0.0"/>
|
||||
</scene>
|
||||
<!--Reminders-->
|
||||
<scene sceneID="fWF-ss-cNz">
|
||||
<objects>
|
||||
<tableViewController id="JYM-cs-i4H" customClass="TVCReminderAlerts" customModule="AppCheck" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="Dop-3B-Uvh">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<sections>
|
||||
<tableViewSection headerTitle="Restart Reminder" id="UOi-fT-8Vh">
|
||||
<string key="footerTitle">If VPN stops accidentally, show a notification 5 minutes later. It will remind you to re-enable the VPN after system reboot.
|
||||
|
||||
if Notification is enabled, show a notification banner once, stating the VPN has stopped.
|
||||
|
||||
If App Badge is enabled, display the letter "1" on the homescreen app icon, as long as the VPN is not running.</string>
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" textLabel="dl8-4J-a0L" style="IBUITableViewCellStyleDefault" id="Z2r-Kz-TCt">
|
||||
<rect key="frame" x="0.0" y="55.5" width="320" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Z2r-Kz-TCt" id="rEy-qO-PEN">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Warn If VPN Stops" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="dl8-4J-a0L">
|
||||
<rect key="frame" x="16" y="0.0" width="288" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" id="zaV-mh-eqb">
|
||||
<rect key="frame" x="252" y="6" width="54" height="31"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
|
||||
<connections>
|
||||
<action selector="toggleAllowRestartReminder:" destination="JYM-cs-i4H" eventType="valueChanged" id="F4e-k2-bni"/>
|
||||
</connections>
|
||||
</switch>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<outlet property="accessoryView" destination="zaV-mh-eqb" id="irZ-hk-KoR"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" textLabel="XC6-mj-vkg" style="IBUITableViewCellStyleDefault" id="5sU-vh-JDf">
|
||||
<rect key="frame" x="0.0" y="99" width="320" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="5sU-vh-JDf" id="MDI-fb-989">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="… with Notification" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="XC6-mj-vkg">
|
||||
<rect key="frame" x="16" y="0.0" width="288" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" id="HaE-En-NH3">
|
||||
<rect key="frame" x="252" y="6" width="54" height="31"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
|
||||
<connections>
|
||||
<action selector="toggleAllowRestartNotify:" destination="JYM-cs-i4H" eventType="valueChanged" id="12C-h5-mrR"/>
|
||||
</connections>
|
||||
</switch>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<outlet property="accessoryView" destination="HaE-En-NH3" id="dld-32-3vc"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" textLabel="wHg-Wo-szR" style="IBUITableViewCellStyleDefault" id="01e-KG-qDH">
|
||||
<rect key="frame" x="0.0" y="142.5" width="320" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="01e-KG-qDH" id="XWV-FF-VxR">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="… with App Badge" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="wHg-Wo-szR">
|
||||
<rect key="frame" x="16" y="0.0" width="288" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" id="N2Q-cU-pkd">
|
||||
<rect key="frame" x="252" y="6" width="54" height="31"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
|
||||
<connections>
|
||||
<action selector="toggleAllowRestartBadge:" destination="JYM-cs-i4H" eventType="valueChanged" id="76l-6y-fOu"/>
|
||||
</connections>
|
||||
</switch>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<outlet property="accessoryView" destination="N2Q-cU-pkd" id="6LN-Zw-4Nf"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="UBT-zI-VNd" detailTextLabel="tj7-1l-bts" style="IBUITableViewCellStyleValue1" id="pAS-8r-oS5">
|
||||
<rect key="frame" x="0.0" y="186" width="320" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="pAS-8r-oS5" id="iMr-Gb-dhX">
|
||||
<rect key="frame" x="0.0" y="0.0" width="293" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Sound" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="UBT-zI-VNd">
|
||||
<rect key="frame" x="16" y="12" width="49.5" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Detail" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="tj7-1l-bts">
|
||||
<rect key="frame" x="241" y="12" width="44" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<segue destination="I37-dZ-c9Q" kind="push" identifier="segueSoundRestartReminder" id="nBh-15-nBq"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="Recording Reminder" footerTitle="Very sporadic reminder. Triggered if your last recording is older than two weeks." id="9Wn-bc-wKX">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" textLabel="u55-7Z-Q0Q" style="IBUITableViewCellStyleDefault" id="87B-MT-J9s">
|
||||
<rect key="frame" x="0.0" y="449" width="320" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="87B-MT-J9s" id="79D-rZ-spg">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Recording Reminder" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="u55-7Z-Q0Q">
|
||||
<rect key="frame" x="16" y="0.0" width="288" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="mTm-Rm-1RQ">
|
||||
<rect key="frame" x="255" y="6" width="49" height="31"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
|
||||
<connections>
|
||||
<action selector="toggleAllowRecordingReminder:" destination="JYM-cs-i4H" eventType="valueChanged" id="unC-Ur-jPM"/>
|
||||
</connections>
|
||||
</switch>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<outlet property="accessoryView" destination="mTm-Rm-1RQ" id="MRe-3h-xfa"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="VYC-xF-awf" detailTextLabel="Ywb-pT-l1W" style="IBUITableViewCellStyleValue1" id="UkL-k7-bB7">
|
||||
<rect key="frame" x="0.0" y="492.5" width="320" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="UkL-k7-bB7" id="fZv-NH-YA3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="293" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Sound" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="VYC-xF-awf">
|
||||
<rect key="frame" x="16" y="12" width="49.5" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Detail" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Ywb-pT-l1W">
|
||||
<rect key="frame" x="241" y="12" width="44" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<segue destination="I37-dZ-c9Q" kind="push" identifier="segueSoundRecordingReminder" id="xKB-i8-J9c"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
</sections>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="JYM-cs-i4H" id="1ji-9q-6qB"/>
|
||||
<outlet property="delegate" destination="JYM-cs-i4H" id="YU7-R1-VjB"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" title="Reminders" id="Z9N-kQ-xhI"/>
|
||||
<connections>
|
||||
<outlet property="recordingAllow" destination="mTm-Rm-1RQ" id="tqz-Pk-pSi"/>
|
||||
<outlet property="recordingSound" destination="UkL-k7-bB7" id="Y9s-fL-cQU"/>
|
||||
<outlet property="restartAllow" destination="zaV-mh-eqb" id="zcQ-1e-i4H"/>
|
||||
<outlet property="restartAllowBadge" destination="N2Q-cU-pkd" id="RPU-f6-Dv3"/>
|
||||
<outlet property="restartAllowNotify" destination="HaE-En-NH3" id="1BN-5h-zNR"/>
|
||||
<outlet property="restartSound" destination="pAS-8r-oS5" id="9Dy-QR-WXq"/>
|
||||
</connections>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="W7H-LK-3wW" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-200" y="768"/>
|
||||
</scene>
|
||||
<!--Sound-->
|
||||
<scene sceneID="3a5-wn-tdm">
|
||||
<objects>
|
||||
<tableViewController id="I37-dZ-c9Q" customClass="TVCChooseAlertTone" customModule="AppCheck" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="w2R-BE-lM4">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<prototypes>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="checkmark" indentationWidth="10" reuseIdentifier="SettingsAlertToneCell" textLabel="O50-Db-5TI" style="IBUITableViewCellStyleDefault" id="38V-eP-HSv">
|
||||
<rect key="frame" x="0.0" y="55.5" width="320" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="38V-eP-HSv" id="hoG-Cy-sHr">
|
||||
<rect key="frame" x="0.0" y="0.0" width="280" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="O50-Db-5TI">
|
||||
<rect key="frame" x="16" y="0.0" width="256" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</prototypes>
|
||||
<sections/>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="I37-dZ-c9Q" id="aji-Ci-VQs"/>
|
||||
<outlet property="delegate" destination="I37-dZ-c9Q" id="YUg-WL-kh8"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" title="Sound" id="5hI-rp-d1Z"/>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="NXc-3P-uoW" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1500" y="768"/>
|
||||
</scene>
|
||||
<!--Connection Alerts-->
|
||||
<scene sceneID="JyV-QU-Dw8">
|
||||
<objects>
|
||||
<tableViewController id="D2a-Po-vDU" customClass="TVCConnectionAlerts" customModule="AppCheck" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="u0e-LW-1Qh">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<sections>
|
||||
<tableViewSection headerTitle="Connection Alerts" footerTitle="Get a notification whenever a specific DNS request occurs." id="4OJ-qA-l8L">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" textLabel="9g7-rO-sIS" style="IBUITableViewCellStyleDefault" id="8hz-pm-rV5">
|
||||
<rect key="frame" x="0.0" y="55.5" width="320" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="8hz-pm-rV5" id="fz3-2p-ave">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Enable Alerts" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="9g7-rO-sIS">
|
||||
<rect key="frame" x="16" y="0.0" width="288" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="who-8G-voz">
|
||||
<rect key="frame" x="256" y="6" width="48" height="31"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
|
||||
<connections>
|
||||
<action selector="toggleShowNotifications:" destination="D2a-Po-vDU" eventType="valueChanged" id="Thg-6R-7wM"/>
|
||||
</connections>
|
||||
</switch>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<outlet property="accessoryView" destination="who-8G-voz" id="6YE-dG-ThI"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="pck-pT-tnX" detailTextLabel="l8v-5i-Zue" style="IBUITableViewCellStyleValue1" id="laE-pg-nAE">
|
||||
<rect key="frame" x="0.0" y="99" width="320" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="laE-pg-nAE" id="157-KR-1R5">
|
||||
<rect key="frame" x="0.0" y="0.0" width="293" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Sound" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="pck-pT-tnX">
|
||||
<rect key="frame" x="16" y="12" width="49.5" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Detail" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="l8v-5i-Zue">
|
||||
<rect key="frame" x="241" y="12" width="44" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<segue destination="I37-dZ-c9Q" kind="push" id="tUF-Kv-koO"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="Operation Mode" footerTitle="Select whether you'd like to manually include or manually exclude specific domains from notifications." id="9fV-UJ-d1S">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="checkmark" indentationWidth="10" textLabel="Lug-Bp-oz0" style="IBUITableViewCellStyleDefault" id="ZMb-xn-r8o">
|
||||
<rect key="frame" x="0.0" y="234" width="320" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="ZMb-xn-r8o" id="eT6-OU-8eU">
|
||||
<rect key="frame" x="0.0" y="0.0" width="280" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Notify only selected lists" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Lug-Bp-oz0">
|
||||
<rect key="frame" x="16" y="0.0" width="256" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="checkmark" indentationWidth="10" textLabel="FRE-W4-dw2" style="IBUITableViewCellStyleDefault" id="47P-B8-Sul">
|
||||
<rect key="frame" x="0.0" y="277.5" width="320" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="47P-B8-Sul" id="RbD-bN-NCj">
|
||||
<rect key="frame" x="0.0" y="0.0" width="280" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Notify all, except selected" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="FRE-W4-dw2">
|
||||
<rect key="frame" x="16" y="0.0" width="256" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="Include" id="Lus-cA-eCF">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="checkmark" indentationWidth="10" textLabel="be7-GU-inU" style="IBUITableViewCellStyleDefault" id="2bN-EB-rDk">
|
||||
<rect key="frame" x="0.0" y="421" width="320" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="2bN-EB-rDk" id="UxC-Sm-W4n">
|
||||
<rect key="frame" x="0.0" y="0.0" width="280" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Blocked Domains" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="be7-GU-inU">
|
||||
<rect key="frame" x="16" y="0.0" width="256" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="checkmark" indentationWidth="10" textLabel="fYg-Mq-C4Q" style="IBUITableViewCellStyleDefault" id="UTd-2r-8c7">
|
||||
<rect key="frame" x="0.0" y="464.5" width="320" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="UTd-2r-8c7" id="Z6Y-qL-4bw">
|
||||
<rect key="frame" x="0.0" y="0.0" width="280" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Domains of List A" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="fYg-Mq-C4Q">
|
||||
<rect key="frame" x="16" y="0.0" width="256" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="checkmark" indentationWidth="10" textLabel="f4F-j9-uiy" style="IBUITableViewCellStyleDefault" id="dk0-Vg-0Zl">
|
||||
<rect key="frame" x="0.0" y="508" width="320" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="dk0-Vg-0Zl" id="69j-ye-ziM">
|
||||
<rect key="frame" x="0.0" y="0.0" width="280" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Domains of List B" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="f4F-j9-uiy">
|
||||
<rect key="frame" x="16" y="0.0" width="256" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="checkmark" indentationWidth="10" textLabel="RiJ-Eq-LiA" style="IBUITableViewCellStyleDefault" id="VTi-I6-dXQ">
|
||||
<rect key="frame" x="0.0" y="551.5" width="320" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="VTi-I6-dXQ" id="PbB-ri-ibd">
|
||||
<rect key="frame" x="0.0" y="0.0" width="280" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Not in Any List" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="RiJ-Eq-LiA">
|
||||
<rect key="frame" x="16" y="0.0" width="256" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="Custom Lists" footerTitle="" id="5bN-ic-93C">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="1fx-L3-esi" detailTextLabel="SAD-ad-RUa" style="IBUITableViewCellStyleValue2" id="fzJ-h6-8Ll">
|
||||
<rect key="frame" x="0.0" y="658.5" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="fzJ-h6-8Ll" id="u6I-My-k4J">
|
||||
<rect key="frame" x="0.0" y="0.0" width="293" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="List A" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="1fx-L3-esi">
|
||||
<rect key="frame" x="16" y="13" width="91" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="0 Domains" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="SAD-ad-RUa">
|
||||
<rect key="frame" x="113" y="13" width="73" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<segue destination="q3B-Yi-1bx" kind="push" identifier="segueFilterListCustomA" id="2ak-aX-wVl"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="bP5-xR-UWP" detailTextLabel="3b0-Aj-9So" style="IBUITableViewCellStyleValue2" id="uTh-Xw-vp2">
|
||||
<rect key="frame" x="0.0" y="702.5" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="uTh-Xw-vp2" id="h1L-kH-yQX">
|
||||
<rect key="frame" x="0.0" y="0.0" width="293" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="List B" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="bP5-xR-UWP">
|
||||
<rect key="frame" x="16" y="13" width="91" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="0 Domains" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="3b0-Aj-9So">
|
||||
<rect key="frame" x="113" y="13" width="73" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<segue destination="q3B-Yi-1bx" kind="push" identifier="segueFilterListCustomB" id="6wc-d1-VYY"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
</sections>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="D2a-Po-vDU" id="4t8-lb-7wO"/>
|
||||
<outlet property="delegate" destination="D2a-Po-vDU" id="I1e-X5-6xm"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" title="Connection Alerts" id="5Re-pU-mt2"/>
|
||||
<connections>
|
||||
<outlet property="cellSound" destination="laE-pg-nAE" id="Qd5-mf-wox"/>
|
||||
<outlet property="listsCustomA" destination="fzJ-h6-8Ll" id="77h-42-70y"/>
|
||||
<outlet property="listsCustomB" destination="uTh-Xw-vp2" id="6OI-pU-Vff"/>
|
||||
<outlet property="showNotifications" destination="who-8G-voz" id="cUz-Bg-ftS"/>
|
||||
</connections>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="1z3-Sx-YAL" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="650" y="384"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<inferredMetricsTieBreakers>
|
||||
<segue reference="tUF-Kv-koO"/>
|
||||
<segue reference="6wc-d1-VYY"/>
|
||||
</inferredMetricsTieBreakers>
|
||||
</document>
|
||||
@@ -1,58 +1,81 @@
|
||||
import Foundation
|
||||
|
||||
enum Prefs {
|
||||
private static func Int(_ key: String) -> Int { UserDefaults.standard.integer(forKey: key) }
|
||||
private static func Int(_ val: Int, _ key: String) { UserDefaults.standard.set(val, forKey: key) }
|
||||
private static func Bool(_ key: String) -> Bool { UserDefaults.standard.bool(forKey: key) }
|
||||
private static func Bool(_ val: Bool, _ key: String) { UserDefaults.standard.set(val, forKey: key) }
|
||||
private static func `Any`(_ key: String) -> Any? { UserDefaults.standard.object(forKey: key) }
|
||||
private static func `Any`(_ val: Any?, _ key: String) { UserDefaults.standard.set(val, forKey: key) }
|
||||
private static var suite: UserDefaults { UserDefaults.standard }
|
||||
|
||||
private static func Int(_ key: String) -> Int { suite.integer(forKey: key) }
|
||||
private static func Int(_ key: String, _ val: Int) { suite.set(val, forKey: key) }
|
||||
private static func Bool(_ key: String) -> Bool { suite.bool(forKey: key) }
|
||||
private static func Bool(_ key: String, _ val: Bool) { suite.set(val, forKey: key) }
|
||||
private static func Str(_ key: String) -> String? { suite.string(forKey: key) }
|
||||
private static func Str(_ key: String, _ val: String?) { suite.set(val, forKey: key) }
|
||||
private static func Obj(_ key: String) -> Any? { suite.object(forKey: key) }
|
||||
private static func Obj(_ key: String, _ val: Any?) { suite.set(val, forKey: key) }
|
||||
|
||||
static func registerDefaults() {
|
||||
suite.register(defaults: [
|
||||
"RecordingReminderEnabled" : true,
|
||||
"contextAnalyisCoOccurrenceTime" : 5,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Tutorial
|
||||
|
||||
extension Prefs {
|
||||
enum DidShowTutorial {
|
||||
static var Welcome: Bool {
|
||||
get { Prefs.Bool("didShowTutorialAppWelcome") }
|
||||
set { Prefs.Bool(newValue, "didShowTutorialAppWelcome") }
|
||||
set { Prefs.Bool("didShowTutorialAppWelcome", newValue) }
|
||||
}
|
||||
static var Recordings: Bool {
|
||||
get { Prefs.Bool("didShowTutorialRecordings") }
|
||||
set { Prefs.Bool(newValue, "didShowTutorialRecordings") }
|
||||
}
|
||||
}
|
||||
enum ContextAnalyis {
|
||||
static var CoOccurrenceTime: Int? {
|
||||
get { Prefs.Any("contextAnalyisCoOccurrenceTime") as? Int }
|
||||
set { Prefs.Any(newValue, "contextAnalyisCoOccurrenceTime") }
|
||||
set { Prefs.Bool("didShowTutorialRecordings", newValue) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Date Filter
|
||||
|
||||
enum DateFilterKind: Int {
|
||||
case Off = 0, LastXMin = 1, ABRange = 2;
|
||||
}
|
||||
enum DateFilterOrderBy: Int {
|
||||
case Date = 0, Name = 1, Count = 2;
|
||||
}
|
||||
|
||||
extension Prefs {
|
||||
enum DateFilter {
|
||||
static var Kind: DateFilterKind {
|
||||
get { DateFilterKind(rawValue: Prefs.Int("dateFilterType"))! }
|
||||
set { Prefs.Int(newValue.rawValue, "dateFilterType") }
|
||||
set { Prefs.Int("dateFilterType", newValue.rawValue) }
|
||||
}
|
||||
/// Default: `0` (disabled)
|
||||
static var LastXMin: Int {
|
||||
get { Prefs.Int("dateFilterLastXMin") }
|
||||
set { Prefs.Int(newValue, "dateFilterLastXMin") }
|
||||
set { Prefs.Int("dateFilterLastXMin", newValue) }
|
||||
}
|
||||
/// Default: `nil` (disabled)
|
||||
static var RangeA: Timestamp? {
|
||||
get { Prefs.Any("dateFilterRangeA") as? Timestamp }
|
||||
set { Prefs.Any(newValue, "dateFilterRangeA") }
|
||||
get { Prefs.Obj("dateFilterRangeA") as? Timestamp }
|
||||
set { Prefs.Obj("dateFilterRangeA", newValue) }
|
||||
}
|
||||
/// Default: `nil` (disabled)
|
||||
static var RangeB: Timestamp? {
|
||||
get { Prefs.Any("dateFilterRangeB") as? Timestamp }
|
||||
set { Prefs.Any(newValue, "dateFilterRangeB") }
|
||||
get { Prefs.Obj("dateFilterRangeB") as? Timestamp }
|
||||
set { Prefs.Obj("dateFilterRangeB", newValue) }
|
||||
}
|
||||
/// default: `.Date`
|
||||
static var OrderBy: DateFilterOrderBy {
|
||||
get { DateFilterOrderBy(rawValue: Prefs.Int("dateFilterOderType"))! }
|
||||
set { Prefs.Int(newValue.rawValue, "dateFilterOderType") }
|
||||
set { Prefs.Int("dateFilterOderType", newValue.rawValue) }
|
||||
}
|
||||
/// default: `false` (Desc)
|
||||
static var OrderAsc: Bool {
|
||||
get { Prefs.Bool("dateFilterOderAsc") }
|
||||
set { Prefs.Bool(newValue, "dateFilterOderAsc") }
|
||||
set { Prefs.Bool("dateFilterOderAsc", newValue) }
|
||||
}
|
||||
|
||||
/// - Returns: Timestamp restriction depending on current selected date filter.
|
||||
@@ -69,9 +92,31 @@ enum Prefs {
|
||||
}
|
||||
}
|
||||
}
|
||||
enum DateFilterKind: Int {
|
||||
case Off = 0, LastXMin = 1, ABRange = 2;
|
||||
|
||||
|
||||
// MARK: - ContextAnalyis
|
||||
|
||||
extension Prefs {
|
||||
enum ContextAnalyis {
|
||||
static var CoOccurrenceTime: Int {
|
||||
get { Prefs.Int("contextAnalyisCoOccurrenceTime") }
|
||||
set { Prefs.Int("contextAnalyisCoOccurrenceTime", newValue) }
|
||||
}
|
||||
}
|
||||
}
|
||||
enum DateFilterOrderBy: Int {
|
||||
case Date = 0, Name = 1, Count = 2;
|
||||
|
||||
|
||||
// MARK: - Notifications
|
||||
|
||||
extension Prefs {
|
||||
enum RecordingReminder {
|
||||
static var Enabled: Bool {
|
||||
get { Prefs.Bool("RecordingReminderEnabled") }
|
||||
set { Prefs.Bool("RecordingReminderEnabled", newValue) }
|
||||
}
|
||||
static var Sound: String {
|
||||
get { Prefs.Str("RecordingReminderSound") ?? "#default" }
|
||||
set { Prefs.Str("RecordingReminderSound", newValue) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,79 @@ enum PrefsShared {
|
||||
private static var suite: UserDefaults { UserDefaults(suiteName: "group.de.uni-bamberg.psi.AppCheck")! }
|
||||
|
||||
private static func Int(_ key: String) -> Int { suite.integer(forKey: key) }
|
||||
private static func Int(_ val: Int, _ key: String) { suite.set(val, forKey: key) }
|
||||
// private static func Obj(_ key: String) -> Any? { suite.object(forKey: key) }
|
||||
// private static func Obj(_ val: Any?, _ key: String) { suite.set(val, forKey: key) }
|
||||
private static func Int(_ key: String, _ val: Int) { suite.set(val, forKey: key); suite.synchronize() }
|
||||
private static func Bool(_ key: String) -> Bool { suite.bool(forKey: key) }
|
||||
private static func Bool(_ key: String, _ val: Bool) { suite.set(val, forKey: key); suite.synchronize() }
|
||||
private static func Str(_ key: String) -> String? { suite.string(forKey: key) }
|
||||
private static func Str(_ key: String, _ val: String?) { suite.set(val, forKey: key); suite.synchronize() }
|
||||
|
||||
static func registerDefaults() {
|
||||
suite.register(defaults: [
|
||||
"RestartReminderEnabled" : true,
|
||||
"RestartReminderWithText" : true,
|
||||
"RestartReminderWithBadge" : true,
|
||||
"ConnectionAlertsListsElse" : true,
|
||||
])
|
||||
}
|
||||
|
||||
static var AutoDeleteLogsDays: Int {
|
||||
get { Int("AutoDeleteLogsDays") }
|
||||
set { Int(newValue, "AutoDeleteLogsDays"); suite.synchronize() }
|
||||
set { Int("AutoDeleteLogsDays", newValue) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Notifications
|
||||
|
||||
extension PrefsShared {
|
||||
enum RestartReminder {
|
||||
static var Enabled: Bool {
|
||||
get { PrefsShared.Bool("RestartReminderEnabled") }
|
||||
set { PrefsShared.Bool("RestartReminderEnabled", newValue) }
|
||||
}
|
||||
static var WithText: Bool {
|
||||
get { PrefsShared.Bool("RestartReminderWithText") }
|
||||
set { PrefsShared.Bool("RestartReminderWithText", newValue) }
|
||||
}
|
||||
static var WithBadge: Bool {
|
||||
get { PrefsShared.Bool("RestartReminderWithBadge") }
|
||||
set { PrefsShared.Bool("RestartReminderWithBadge", newValue) }
|
||||
}
|
||||
static var Sound: String {
|
||||
get { PrefsShared.Str("RestartReminderSound") ?? "#default" }
|
||||
set { PrefsShared.Str("RestartReminderSound", newValue) }
|
||||
}
|
||||
}
|
||||
enum ConnectionAlerts {
|
||||
static var Enabled: Bool {
|
||||
get { PrefsShared.Bool("ConnectionAlertsEnabled") }
|
||||
set { PrefsShared.Bool("ConnectionAlertsEnabled", newValue) }
|
||||
}
|
||||
static var Sound: String {
|
||||
get { PrefsShared.Str("ConnectionAlertsSound") ?? "#default" }
|
||||
set { PrefsShared.Str("ConnectionAlertsSound", newValue) }
|
||||
}
|
||||
static var ExcludeMode: Bool {
|
||||
get { PrefsShared.Bool("ConnectionAlertsExcludeMode") }
|
||||
set { PrefsShared.Bool("ConnectionAlertsExcludeMode", newValue) }
|
||||
}
|
||||
enum Lists {
|
||||
static var CustomA: Bool {
|
||||
get { PrefsShared.Bool("ConnectionAlertsListsCustomA") }
|
||||
set { PrefsShared.Bool("ConnectionAlertsListsCustomA", newValue) }
|
||||
}
|
||||
static var CustomB: Bool {
|
||||
get { PrefsShared.Bool("ConnectionAlertsListsCustomB") }
|
||||
set { PrefsShared.Bool("ConnectionAlertsListsCustomB", newValue) }
|
||||
}
|
||||
static var Blocked: Bool {
|
||||
get { PrefsShared.Bool("ConnectionAlertsListsBlocked") }
|
||||
set { PrefsShared.Bool("ConnectionAlertsListsBlocked", newValue) }
|
||||
}
|
||||
static var Else: Bool {
|
||||
get { PrefsShared.Bool("ConnectionAlertsListsElse") }
|
||||
set { PrefsShared.Bool("ConnectionAlertsListsElse", newValue) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
153
main/Common Classes/PushNotification.swift
Normal file
153
main/Common Classes/PushNotification.swift
Normal file
@@ -0,0 +1,153 @@
|
||||
import UserNotifications
|
||||
|
||||
struct PushNotification {
|
||||
|
||||
enum Identifier: String {
|
||||
case YouShallRecordMoreReminder
|
||||
case CantStopMeNowReminder
|
||||
case RestInPeaceTombstone
|
||||
case AllConnectionAlertNotifications
|
||||
}
|
||||
|
||||
static func allowed(_ closure: @escaping (NotificationRequestState) -> Void) {
|
||||
guard #available(iOS 10, *) else { return }
|
||||
|
||||
UNUserNotificationCenter.current().getNotificationSettings { settings in
|
||||
let state = NotificationRequestState(settings.authorizationStatus)
|
||||
DispatchQueue.main.async {
|
||||
closure(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Available in iOS 12+
|
||||
static func requestProvisionalOrDoNothing(_ closure: @escaping (Bool) -> Void) {
|
||||
guard #available(iOS 12, *) else { return closure(false) }
|
||||
|
||||
let opt: UNAuthorizationOptions = [.alert, .sound, .badge, .provisional, .providesAppNotificationSettings]
|
||||
UNUserNotificationCenter.current().requestAuthorization(options: opt) { granted, _ in
|
||||
DispatchQueue.main.async {
|
||||
closure(granted)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func requestAuthorization(_ closure: @escaping (Bool) -> Void) {
|
||||
guard #available(iOS 10, *) else { return }
|
||||
|
||||
var opt: UNAuthorizationOptions = [.alert, .sound, .badge]
|
||||
if #available(iOS 12, *) {
|
||||
opt.formUnion(.providesAppNotificationSettings)
|
||||
}
|
||||
UNUserNotificationCenter.current().requestAuthorization(options: opt) { granted, _ in
|
||||
DispatchQueue.main.async {
|
||||
closure(granted)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func hasPending(_ ident: Identifier, _ closure: @escaping (Bool) -> Void) {
|
||||
guard #available(iOS 10, *) else { return }
|
||||
|
||||
UNUserNotificationCenter.current().getPendingNotificationRequests {
|
||||
let hasIt = $0.contains { $0.identifier == ident.rawValue }
|
||||
DispatchQueue.main.async {
|
||||
closure(hasIt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func cancel(_ ident: Identifier, keepDelivered: Bool = false) {
|
||||
guard #available(iOS 10, *) else { return }
|
||||
|
||||
let center = UNUserNotificationCenter.current()
|
||||
guard ident != .AllConnectionAlertNotifications else {
|
||||
// remove all connection alert notifications while
|
||||
// keeping general purpose reminder notifications
|
||||
center.getDeliveredNotifications {
|
||||
var list = $0.map { $0.request.identifier }
|
||||
list.removeAll { !$0.contains(".") } // each domain (or IP) has a dot
|
||||
center.removeDeliveredNotifications(withIdentifiers: list)
|
||||
// no need to do the same for pending since con-alerts are always immediate
|
||||
}
|
||||
return
|
||||
}
|
||||
center.removePendingNotificationRequests(withIdentifiers: [ident.rawValue])
|
||||
if !keepDelivered {
|
||||
center.removeDeliveredNotifications(withIdentifiers: [ident.rawValue])
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 10.0, *)
|
||||
static func schedule(_ ident: Identifier, content: UNNotificationContent, trigger: UNNotificationTrigger? = nil, waitUntilDone: Bool = false) {
|
||||
schedule(ident.rawValue, content: content, trigger: trigger, waitUntilDone: waitUntilDone)
|
||||
}
|
||||
|
||||
@available(iOS 10.0, *)
|
||||
static func schedule(_ ident: String, content: UNNotificationContent, trigger: UNNotificationTrigger? = nil, waitUntilDone: Bool = false) {
|
||||
let req = UNNotificationRequest(identifier: ident, content: content, trigger: trigger)
|
||||
waitUntilDone ? req.pushAndWait() : req.push()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Reminder Alerts
|
||||
|
||||
extension PushNotification {
|
||||
/// Auto-check preferences whether `withText` is set, then schedule notification to 5 min in the future.
|
||||
static func scheduleRestartReminderBanner() {
|
||||
guard #available(iOS 10, *), PrefsShared.RestartReminder.WithText else { return }
|
||||
|
||||
schedule(.CantStopMeNowReminder,
|
||||
content: .make("AppCheck disabled",
|
||||
body: "AppCheck can't monitor network traffic because VPN has stopped.",
|
||||
sound: .from(string: PrefsShared.RestartReminder.Sound)),
|
||||
trigger: .make(Date(timeIntervalSinceNow: 5 * 60)),
|
||||
waitUntilDone: true)
|
||||
}
|
||||
|
||||
/// Auto-check preferences whether `withBadge` is set, then post badge immediatelly.
|
||||
/// - Parameter on: If `true`, set `1` on app icon. If `false`, remove badge on app icon.
|
||||
static func scheduleRestartReminderBadge(on: Bool) {
|
||||
guard #available(iOS 10, *), PrefsShared.RestartReminder.WithBadge else { return }
|
||||
|
||||
schedule(.RestInPeaceTombstone, content: .makeBadge(on ? 1 : 0), waitUntilDone: true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Connection Alerts
|
||||
|
||||
extension PushNotification {
|
||||
static private let queue = ThrottledBatchQueue<String>(0.5, using: .init(label: "PSINotificationQueue", qos: .default, target: .global()))
|
||||
|
||||
/// Post new notification with given domain name. If notification already exists, increase occurrence count.
|
||||
/// - Parameter domain: Used in the description and as notification identifier.
|
||||
@available(iOS 10.0, *)
|
||||
static func scheduleConnectionAlert(_ domain: String, sound: UNNotificationSound?) {
|
||||
queue.addDelayed(domain) { batch in
|
||||
let groupSum = batch.reduce(into: [:]) { $0[$1] = ($0[$1] ?? 0) + 1 }
|
||||
scheduleConnectionAlertMulti(groupSum, sound: sound)
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal method to post a batch of counted domains.
|
||||
@available(iOS 10.0, *)
|
||||
static private func scheduleConnectionAlertMulti(_ group: [String: Int], sound: UNNotificationSound?) {
|
||||
UNUserNotificationCenter.current().getDeliveredNotifications { delivered in
|
||||
for (dom, count) in group {
|
||||
let num: Int
|
||||
if let prev = delivered.first(where: { $0.request.identifier == dom })?.request.content {
|
||||
if let p = prev.body.split(separator: "×").first, let i = Int(p) {
|
||||
num = count + i
|
||||
} else {
|
||||
num = count + 1
|
||||
}
|
||||
} else {
|
||||
num = count
|
||||
}
|
||||
schedule(dom, content: .make("DNS connection", body: num > 1 ? "\(num)× \(dom)" : dom, sound: sound))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
28
main/Common Classes/PushNotificationAppOnly.swift
Normal file
28
main/Common Classes/PushNotificationAppOnly.swift
Normal file
@@ -0,0 +1,28 @@
|
||||
import UserNotifications
|
||||
|
||||
extension PushNotification {
|
||||
static func scheduleRecordingReminder(force: Bool) {
|
||||
if force {
|
||||
scheduleRecordingReminder()
|
||||
} else {
|
||||
hasPending(.YouShallRecordMoreReminder) {
|
||||
if !$0 { scheduleRecordingReminder() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static func scheduleRecordingReminder() {
|
||||
guard #available(iOS 10, *) else { return }
|
||||
|
||||
let now = Timestamp.now()
|
||||
var next = RecordingsDB.lastTimestamp() ?? (now - 1)
|
||||
while next < now {
|
||||
next += .days(14)
|
||||
}
|
||||
schedule(.YouShallRecordMoreReminder,
|
||||
content: .make("Start new recording",
|
||||
body: "It's been a while since your last recording …",
|
||||
sound: .from(string: Prefs.RecordingReminder.Sound)),
|
||||
trigger: .make(Date(next)))
|
||||
}
|
||||
}
|
||||
31
main/Common Classes/ThrottledBatchQueue.swift
Normal file
31
main/Common Classes/ThrottledBatchQueue.swift
Normal file
@@ -0,0 +1,31 @@
|
||||
import Foundation
|
||||
|
||||
class ThrottledBatchQueue<T> {
|
||||
private var cache: [T] = []
|
||||
private var scheduled: Bool = false
|
||||
private let queue: DispatchQueue
|
||||
private let delay: Double
|
||||
|
||||
init(_ delay: Double, using queue: DispatchQueue) {
|
||||
self.queue = queue
|
||||
self.delay = delay
|
||||
}
|
||||
|
||||
func addDelayed(_ elem: T, afterDelay closure: @escaping ([T]) -> Void) {
|
||||
queue.sync {
|
||||
cache.append(elem)
|
||||
guard !scheduled else {
|
||||
return
|
||||
}
|
||||
scheduled = true
|
||||
queue.asyncAfter(deadline: .now() + delay) {
|
||||
let aCopy = self.cache
|
||||
self.cache.removeAll(keepingCapacity: true)
|
||||
self.scheduled = false
|
||||
DispatchQueue.main.async {
|
||||
closure(aCopy)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -382,6 +382,14 @@ extension SQLiteDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get `Timestamp` of last recording.
|
||||
func recordingLastTimestamp() -> Timestamp? {
|
||||
try? run(sql: "SELECT stop FROM rec WHERE stop IS NOT NULL ORDER BY rowid DESC LIMIT 1;") {
|
||||
try ifStep($0, SQLITE_ROW)
|
||||
return col_ts($0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
/// `WHERE stop IS NOT NULL`
|
||||
func recordingGetAll() -> [Recording]? {
|
||||
try? run(sql: "SELECT * FROM rec WHERE stop IS NOT NULL;") {
|
||||
|
||||
@@ -74,7 +74,9 @@ struct FilterOptions: OptionSet {
|
||||
static let none = FilterOptions([])
|
||||
static let blocked = FilterOptions(rawValue: 1 << 0)
|
||||
static let ignored = FilterOptions(rawValue: 1 << 1)
|
||||
static let any = FilterOptions(rawValue: 0b11)
|
||||
static let customA = FilterOptions(rawValue: 1 << 2)
|
||||
static let customB = FilterOptions(rawValue: 1 << 3)
|
||||
static let any = FilterOptions(rawValue: 0b1111)
|
||||
}
|
||||
|
||||
extension SQLiteDatabase {
|
||||
|
||||
@@ -21,10 +21,13 @@ enum DomainFilter {
|
||||
}
|
||||
|
||||
/// Get total number of blocked and ignored domains. Shown in settings overview.
|
||||
static func counts() -> (blocked: Int, ignored: Int) {
|
||||
data.reduce(into: (0, 0)) {
|
||||
static func counts() -> (blocked: Int, ignored: Int, listCustomA: Int, listCustomB: Int) {
|
||||
data.reduce(into: (0, 0, 0, 0)) {
|
||||
if $1.1.contains(.blocked) { $0.0 += 1 }
|
||||
if $1.1.contains(.ignored) { $0.1 += 1 } }
|
||||
if $1.1.contains(.ignored) { $0.1 += 1 }
|
||||
if $1.1.contains(.customA) { $0.2 += 1 }
|
||||
if $1.1.contains(.customB) { $0.3 += 1 }
|
||||
}
|
||||
}
|
||||
|
||||
/// Union `filter` with set.
|
||||
|
||||
@@ -13,6 +13,9 @@ enum RecordingsDB {
|
||||
/// Get list of all recordings
|
||||
static func list() -> [Recording] { AppDB?.recordingGetAll() ?? [] }
|
||||
|
||||
/// Get `Timestamp` of latest recording
|
||||
static func lastTimestamp() -> Timestamp? { AppDB?.recordingLastTimestamp() }
|
||||
|
||||
/// Copy log entries from generic `heap` table to recording specific `recLog` table
|
||||
static func persist(_ r: Recording) {
|
||||
sync.syncNow { // persist changes in cache before copying recording details
|
||||
|
||||
@@ -37,6 +37,13 @@ func AskAlert(title: String?, text: String?, buttonText: String = "Continue", bu
|
||||
return alert
|
||||
}
|
||||
|
||||
/// Show alert hinting the user to go to system settings and re-enable notifications.
|
||||
func NotificationsDisabledAlert(presentIn viewController: UIViewController) {
|
||||
Alert(title: "Notifications Disabled",
|
||||
text: "Go to System Settings > Notifications > AppCheck to re-enable notifications."
|
||||
).presentIn(viewController)
|
||||
}
|
||||
|
||||
// MARK: Alert with multiple options
|
||||
|
||||
/// - Parameters:
|
||||
|
||||
@@ -23,6 +23,8 @@ extension Timestamp {
|
||||
static func minutes(_ m: Int) -> Timestamp { Timestamp(m * 60) }
|
||||
/// Create `Timestamp` with `h * 3600` seconds
|
||||
static func hours(_ h: Int) -> Timestamp { Timestamp(h * 3600) }
|
||||
/// Create `Timestamp` with `d * 86400` seconds
|
||||
static func days(_ d: Int) -> Timestamp { Timestamp(d * 86400) }
|
||||
}
|
||||
|
||||
extension Timer {
|
||||
|
||||
83
main/Extensions/UNNotification.swift
Normal file
83
main/Extensions/UNNotification.swift
Normal file
@@ -0,0 +1,83 @@
|
||||
import UserNotifications
|
||||
|
||||
enum NotificationRequestState {
|
||||
case NotDetermined, Denied, Authorized, Provisional
|
||||
@available(iOS 10.0, *)
|
||||
init(_ from: UNAuthorizationStatus) {
|
||||
switch from {
|
||||
case .notDetermined: self = .NotDetermined
|
||||
case .denied: self = .Denied
|
||||
case .authorized: self = .Authorized
|
||||
case .provisional: self = .Provisional
|
||||
@unknown default: fatalError()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 10.0, *)
|
||||
extension UNNotificationRequest {
|
||||
func push() {
|
||||
UNUserNotificationCenter.current().add(self) { error in
|
||||
if let e = error {
|
||||
NSLog("[ERROR] Can't add push notification: \(e)")
|
||||
}
|
||||
}
|
||||
}
|
||||
func pushAndWait() {
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
UNUserNotificationCenter.current().add(self) { error in
|
||||
if let e = error {
|
||||
NSLog("[ERROR] Can't add push notification: \(e)")
|
||||
}
|
||||
semaphore.signal()
|
||||
}
|
||||
_ = semaphore.wait(wallTimeout: .distantFuture)
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 10.0, *)
|
||||
extension UNNotificationContent {
|
||||
/// - Parameter sound: Use `#default` or `nil` to play the default tone. Use `#mute` to play no tone at all. Else use an `UNNotificationSoundName`.
|
||||
static func make(_ title: String, body: String, sound: UNNotificationSound? = .default) -> UNNotificationContent {
|
||||
let x = UNMutableNotificationContent()
|
||||
// use NSString.localizedUserNotificationString(forKey:arguments:)
|
||||
x.title = title
|
||||
x.body = body
|
||||
x.sound = sound
|
||||
return x
|
||||
}
|
||||
/// - Parameter value: `0` will remove the badge
|
||||
static func makeBadge(_ value: Int) -> UNNotificationContent {
|
||||
let x = UNMutableNotificationContent()
|
||||
x.badge = value as NSNumber?
|
||||
return x
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 10.0, *)
|
||||
extension UNNotificationTrigger {
|
||||
/// Calls `(dateMatching: components, repeats: repeats)`
|
||||
static func make(_ components: DateComponents, repeats: Bool) -> UNCalendarNotificationTrigger {
|
||||
UNCalendarNotificationTrigger(dateMatching: components, repeats: repeats)
|
||||
}
|
||||
/// Calls `(dateMatching: components(second-year), repeats: false)`
|
||||
static func make(_ date: Date) -> UNCalendarNotificationTrigger {
|
||||
let components = Calendar.current.dateComponents([.year,.month,.day,.hour,.minute,.second], from: date)
|
||||
return UNCalendarNotificationTrigger(dateMatching: components, repeats: false)
|
||||
}
|
||||
/// Calls `(timeInterval: time, repeats: repeats)`
|
||||
static func make(_ time: TimeInterval, repeats: Bool) -> UNTimeIntervalNotificationTrigger {
|
||||
UNTimeIntervalNotificationTrigger(timeInterval: time, repeats: repeats)
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 10.0, *)
|
||||
extension UNNotificationSound {
|
||||
static func from(string: String) -> UNNotificationSound? {
|
||||
switch string {
|
||||
case "#mute": return nil
|
||||
case "#default": return .default
|
||||
case let name: return .init(named: UNNotificationSoundName(name + ".caf"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -136,4 +136,8 @@ struct VPNAppMessage {
|
||||
static func autoDelete(after interval: Int) -> Self {
|
||||
.init("auto-delete:\(interval)")
|
||||
}
|
||||
/// Only used for connection alert notifications
|
||||
static func notificationSettingsChanged() -> Self {
|
||||
.init("notify-prefs-change:1")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,9 @@ class VCEditRecording: UIViewController, UITextFieldDelegate, UITextViewDelegate
|
||||
RecordingsDB.update(self.record)
|
||||
if newlyCreated {
|
||||
RecordingsDB.persist(self.record)
|
||||
if Prefs.RecordingReminder.Enabled {
|
||||
PushNotification.scheduleRecordingReminder(force: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ class VCCoOccurrence: UIViewController, UITableViewDataSource {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
selectedTime = Prefs.ContextAnalyis.CoOccurrenceTime ?? 5 // calls `didSet` and `logTimeDelta`
|
||||
selectedTime = Prefs.ContextAnalyis.CoOccurrenceTime // calls `didSet` and `logTimeDelta`
|
||||
timeSegment.removeAllSegments() // clear IB values
|
||||
for (i, time) in availableTimes.enumerated() {
|
||||
timeSegment.insertSegment(withTitle: TimeFormat(.abbreviated).from(seconds: time), at: i, animated: false)
|
||||
|
||||
97
main/Settings/TVCChooseAlertTone.swift
Normal file
97
main/Settings/TVCChooseAlertTone.swift
Normal file
@@ -0,0 +1,97 @@
|
||||
import UIKit
|
||||
import AudioToolbox
|
||||
|
||||
protocol NotificationSoundChangedDelegate {
|
||||
/// Use `#mute` to disable sounds and `#default` to use default notification sound.
|
||||
func notificationSoundCurrent() -> String
|
||||
/// Called every time the user changes selection
|
||||
func notificationSoundChanged(filename: String, title: String)
|
||||
}
|
||||
|
||||
class TVCChooseAlertTone: UITableViewController {
|
||||
|
||||
var delegate: NotificationSoundChangedDelegate!
|
||||
private lazy var selected: String = delegate.notificationSoundCurrent()
|
||||
|
||||
private func playTone(_ name: String) {
|
||||
switch name {
|
||||
case "#mute": return // No Sound
|
||||
case "#default": AudioServicesPlayAlertSound(1315) // Default sound
|
||||
default:
|
||||
guard let url = Bundle.main.url(forResource: name, withExtension: "caf") else {
|
||||
preconditionFailure("Something went wrong. Sound file \(name).caf does not exist.")
|
||||
}
|
||||
var soundId: SystemSoundID = 0
|
||||
AudioServicesCreateSystemSoundID(url as CFURL, &soundId)
|
||||
AudioServicesAddSystemSoundCompletion(soundId, nil, nil, { id, _ -> Void in
|
||||
AudioServicesDisposeSystemSoundID(id)
|
||||
}, nil)
|
||||
AudioServicesPlayAlertSound(soundId)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Table View Delegate
|
||||
|
||||
override func numberOfSections(in _: UITableView) -> Int {
|
||||
AvailableSounds.count
|
||||
}
|
||||
|
||||
override func tableView(_: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
AvailableSounds[section].count
|
||||
}
|
||||
|
||||
override func tableView(_: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
section == 1 ? "AppCheck" : nil
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "SettingsAlertToneCell")!
|
||||
let src = AvailableSounds[indexPath.section][indexPath.row]
|
||||
cell.textLabel?.text = src.title
|
||||
cell.accessoryType = (src.file == selected) ? .checkmark : .none
|
||||
return cell
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
|
||||
let src = AvailableSounds[indexPath.section][indexPath.row]
|
||||
selected = src.file
|
||||
tableView.reloadData() // re-apply checkmarks
|
||||
playTone(selected)
|
||||
delegate.notificationSoundChanged(filename: src.file, title: src.title)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Sounds Data Source
|
||||
// afconvert input.aiff output.caf -d ima4 -f caff -v
|
||||
|
||||
fileprivate let AvailableSounds: [[(title: String, file: String)]] = [
|
||||
[ // System sounds
|
||||
("None", "#mute"),
|
||||
("Default", "#default")
|
||||
], [ // AppCheck sounds
|
||||
("Clock", "clock"),
|
||||
("Drum 1", "drum1"),
|
||||
("Drum 2", "drum2"),
|
||||
("Plop 1", "plop1"),
|
||||
("Plop 2", "plop2"),
|
||||
("Snap 1", "snap1"),
|
||||
("Snap 2", "snap2"),
|
||||
("Typewriter 1", "typewriter1"),
|
||||
("Typewriter 2", "typewriter2"),
|
||||
("Wood 1", "wood1"),
|
||||
("Wood 2", "wood2")
|
||||
]
|
||||
]
|
||||
|
||||
func AlertSoundTitle(for filename: String) -> String {
|
||||
for section in AvailableSounds {
|
||||
for row in section {
|
||||
if row.file == filename {
|
||||
return row.title
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
135
main/Settings/TVCConnectionAlerts.swift
Normal file
135
main/Settings/TVCConnectionAlerts.swift
Normal file
@@ -0,0 +1,135 @@
|
||||
import UIKit
|
||||
|
||||
class TVCConnectionAlerts: UITableViewController {
|
||||
|
||||
@IBOutlet var showNotifications: UISwitch!
|
||||
@IBOutlet var cellSound: UITableViewCell!
|
||||
|
||||
@IBOutlet var listsCustomA: UITableViewCell!
|
||||
@IBOutlet var listsCustomB: UITableViewCell!
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
cascadeEnableConnAlert(PrefsShared.ConnectionAlerts.Enabled)
|
||||
cellSound.detailTextLabel?.text = AlertSoundTitle(for: PrefsShared.ConnectionAlerts.Sound)
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
let (_, _, custA, custB) = DomainFilter.counts()
|
||||
listsCustomA.detailTextLabel?.text = "\(custA) Domains"
|
||||
listsCustomB.detailTextLabel?.text = "\(custB) Domains"
|
||||
}
|
||||
|
||||
private func cascadeEnableConnAlert(_ flag: Bool) {
|
||||
showNotifications.isOn = flag
|
||||
// en/disable related controls
|
||||
}
|
||||
|
||||
private func getListSelected(_ index: Int) -> Bool {
|
||||
switch index {
|
||||
case 0: return PrefsShared.ConnectionAlerts.Lists.Blocked
|
||||
case 1: return PrefsShared.ConnectionAlerts.Lists.CustomA
|
||||
case 2: return PrefsShared.ConnectionAlerts.Lists.CustomB
|
||||
case 3: return PrefsShared.ConnectionAlerts.Lists.Else
|
||||
default: preconditionFailure()
|
||||
}
|
||||
}
|
||||
|
||||
private func setListSelected(_ index: Int, _ value: Bool) {
|
||||
switch index {
|
||||
case 0: PrefsShared.ConnectionAlerts.Lists.Blocked = value
|
||||
case 1: PrefsShared.ConnectionAlerts.Lists.CustomA = value
|
||||
case 2: PrefsShared.ConnectionAlerts.Lists.CustomB = value
|
||||
case 3: PrefsShared.ConnectionAlerts.Lists.Else = value
|
||||
default: preconditionFailure()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Toggles
|
||||
|
||||
@IBAction private func toggleShowNotifications(_ sender: UISwitch) {
|
||||
PrefsShared.ConnectionAlerts.Enabled = sender.isOn
|
||||
cascadeEnableConnAlert(sender.isOn)
|
||||
GlassVPN.send(.notificationSettingsChanged())
|
||||
if sender.isOn {
|
||||
PushNotification.requestAuthorization { granted in
|
||||
if !granted {
|
||||
NotificationsDisabledAlert(presentIn: self)
|
||||
self.cascadeEnableConnAlert(false)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
PushNotification.cancel(.AllConnectionAlertNotifications)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Table View Delegate
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = super.tableView(tableView, cellForRowAt: indexPath)
|
||||
let checked: Bool
|
||||
switch indexPath.section {
|
||||
case 1: // mode selection
|
||||
checked = (indexPath.row == (PrefsShared.ConnectionAlerts.ExcludeMode ? 1 : 0))
|
||||
case 2: // include & exclude lists
|
||||
checked = getListSelected(indexPath.row)
|
||||
default: return cell // process only checkmarked cells
|
||||
}
|
||||
cell.accessoryType = checked ? .checkmark : .none
|
||||
return cell
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
switch indexPath.section {
|
||||
case 1: // mode selection
|
||||
PrefsShared.ConnectionAlerts.ExcludeMode = indexPath.row == 1
|
||||
tableView.reloadSections(.init(integer: 2), with: .none)
|
||||
case 2: // include & exclude lists
|
||||
let prev = tableView.cellForRow(at: indexPath)?.accessoryType == .checkmark
|
||||
setListSelected(indexPath.row, !prev)
|
||||
default: return // process only checkmarked cells
|
||||
}
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
tableView.reloadSections(.init(integer: indexPath.section), with: .none)
|
||||
GlassVPN.send(.notificationSettingsChanged())
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
if section == 2 {
|
||||
return PrefsShared.ConnectionAlerts.ExcludeMode ? "Exclude All" : "Include All"
|
||||
}
|
||||
return super.tableView(tableView, titleForHeaderInSection: section)
|
||||
}
|
||||
|
||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||
if let dest = segue.destination as? TVCFilter {
|
||||
switch segue.identifier {
|
||||
case "segueFilterListCustomA":
|
||||
dest.navigationItem.title = "Custom List A"
|
||||
dest.currentFilter = .customA
|
||||
case "segueFilterListCustomB":
|
||||
dest.navigationItem.title = "Custom List B"
|
||||
dest.currentFilter = .customB
|
||||
default:
|
||||
break
|
||||
}
|
||||
} else if let tvc = segue.destination as? TVCChooseAlertTone {
|
||||
tvc.delegate = self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Sound Selection
|
||||
|
||||
extension TVCConnectionAlerts: NotificationSoundChangedDelegate {
|
||||
func notificationSoundCurrent() -> String {
|
||||
PrefsShared.ConnectionAlerts.Sound
|
||||
}
|
||||
|
||||
func notificationSoundChanged(filename: String, title: String) {
|
||||
cellSound.detailTextLabel?.text = title
|
||||
PrefsShared.ConnectionAlerts.Sound = filename
|
||||
GlassVPN.send(.notificationSettingsChanged())
|
||||
}
|
||||
}
|
||||
@@ -25,14 +25,10 @@ class TVCFilter: UITableViewController, EditActionsRemove {
|
||||
}
|
||||
|
||||
@IBAction private func addNewFilter() {
|
||||
let desc: String
|
||||
switch currentFilter {
|
||||
case .blocked: desc = "Enter the domain name you wish to block."
|
||||
case .ignored: desc = "Enter the domain name you wish to ignore."
|
||||
default: return
|
||||
}
|
||||
let alert = AskAlert(title: "Create new filter", text: desc, buttonText: "Add") {
|
||||
guard let dom = $0.textFields?.first?.text else {
|
||||
let alert = AskAlert(title: "Create new filter",
|
||||
text: "Enter the domain name you wish to add.",
|
||||
buttonText: "Add") {
|
||||
guard let dom = $0.textFields?.first?.text?.lowercased() else {
|
||||
return
|
||||
}
|
||||
guard dom.contains("."), !dom.isKnownSLD() else {
|
||||
|
||||
154
main/Settings/TVCReminderAlerts.swift
Normal file
154
main/Settings/TVCReminderAlerts.swift
Normal file
@@ -0,0 +1,154 @@
|
||||
import UIKit
|
||||
|
||||
class TVCReminderAlerts: UITableViewController {
|
||||
|
||||
@IBOutlet var restartAllow: UISwitch!
|
||||
@IBOutlet var restartAllowNotify: UISwitch!
|
||||
@IBOutlet var restartAllowBadge: UISwitch!
|
||||
@IBOutlet var restartSound: UITableViewCell!
|
||||
|
||||
@IBOutlet var recordingAllow: UISwitch!
|
||||
@IBOutlet var recordingSound: UITableViewCell!
|
||||
|
||||
private enum ReminderCellType { case Restart, Recording }
|
||||
private var selectedSound: ReminderCellType = .Restart
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
restartAllowNotify.isOn = PrefsShared.RestartReminder.WithText
|
||||
restartAllowBadge.isOn = PrefsShared.RestartReminder.WithBadge
|
||||
restartSound.detailTextLabel?.text = AlertSoundTitle(for: PrefsShared.RestartReminder.Sound)
|
||||
recordingSound.detailTextLabel?.text = AlertSoundTitle(for: Prefs.RecordingReminder.Sound)
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
readNotificationState { (allowStart, allowRecord, isProvisional) in
|
||||
self.cascadeEnableRestart(allowStart && !isProvisional)
|
||||
self.recordingAllow.isOn = (allowRecord && !isProvisional)
|
||||
self.setIndicateProvisional(isProvisional)
|
||||
}
|
||||
}
|
||||
|
||||
private func readNotificationState(_ closure: @escaping (Bool, Bool, Bool) -> Void) {
|
||||
let en1 = PrefsShared.RestartReminder.Enabled
|
||||
let en2 = Prefs.RecordingReminder.Enabled
|
||||
closure(en1, en2, false)
|
||||
guard en1 || en2 else { return }
|
||||
PushNotification.allowed { state in
|
||||
switch state {
|
||||
case .NotDetermined, .Denied: closure(false, false, false)
|
||||
case .Authorized, .Provisional: closure(en1, en2, state == .Provisional)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func cascadeEnableRestart(_ flag: Bool) {
|
||||
restartAllow.isOn = flag
|
||||
restartAllowNotify.isEnabled = flag
|
||||
restartAllowBadge.isEnabled = flag
|
||||
}
|
||||
|
||||
private func setIndicateProvisional(_ flag: Bool) {
|
||||
if flag {
|
||||
restartAllow.thumbTintColor = .systemGreen
|
||||
recordingAllow.thumbTintColor = .systemGreen
|
||||
} else {
|
||||
// thumb tint is only set in provisional mode
|
||||
if restartAllow.thumbTintColor <-? nil { restartAllow.isOn = true }
|
||||
if recordingAllow.thumbTintColor <-? nil { recordingAllow.isOn = true }
|
||||
}
|
||||
}
|
||||
|
||||
private func updateBadge() {
|
||||
let flag = (restartAllow.isOn && restartAllowBadge.isOn && GlassVPN.state != .on)
|
||||
UIApplication.shared.applicationIconBadgeNumber = flag ? 1 : 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Toggles
|
||||
|
||||
extension TVCReminderAlerts {
|
||||
@IBAction private func toggleAllowRestartReminder(_ sender: UISwitch) {
|
||||
PrefsShared.RestartReminder.Enabled = sender.isOn
|
||||
cascadeEnableRestart(sender.isOn)
|
||||
updateBadge()
|
||||
if sender.isOn {
|
||||
askAuthorization {}
|
||||
} else {
|
||||
PushNotification.cancel(.CantStopMeNowReminder)
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction private func toggleAllowRestartNotify(_ sender: UISwitch) {
|
||||
PrefsShared.RestartReminder.WithText = sender.isOn
|
||||
if !sender.isOn {
|
||||
PushNotification.cancel(.CantStopMeNowReminder)
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction private func toggleAllowRestartBadge(_ sender: UISwitch) {
|
||||
PrefsShared.RestartReminder.WithBadge = sender.isOn
|
||||
updateBadge()
|
||||
}
|
||||
|
||||
@IBAction private func toggleAllowRecordingReminder(_ sender: UISwitch) {
|
||||
Prefs.RecordingReminder.Enabled = sender.isOn
|
||||
if sender.isOn {
|
||||
askAuthorization { PushNotification.scheduleRecordingReminder(force: false) }
|
||||
} else {
|
||||
PushNotification.cancel(.YouShallRecordMoreReminder)
|
||||
}
|
||||
}
|
||||
|
||||
private func askAuthorization(_ closure: @escaping () -> Void) {
|
||||
setIndicateProvisional(false)
|
||||
PushNotification.requestAuthorization { granted in
|
||||
if granted {
|
||||
closure()
|
||||
} else {
|
||||
NotificationsDisabledAlert(presentIn: self)
|
||||
self.cascadeEnableRestart(false)
|
||||
self.recordingAllow.isOn = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Sound Selection
|
||||
|
||||
extension TVCReminderAlerts: NotificationSoundChangedDelegate {
|
||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||
if let tvc = segue.destination as? TVCChooseAlertTone {
|
||||
switch segue.identifier {
|
||||
case "segueSoundRestartReminder": selectedSound = .Restart
|
||||
case "segueSoundRecordingReminder": selectedSound = .Recording
|
||||
default: preconditionFailure()
|
||||
}
|
||||
tvc.delegate = self
|
||||
}
|
||||
}
|
||||
|
||||
func notificationSoundCurrent() -> String {
|
||||
switch selectedSound {
|
||||
case .Restart: return PrefsShared.RestartReminder.Sound
|
||||
case .Recording: return Prefs.RecordingReminder.Sound
|
||||
}
|
||||
}
|
||||
|
||||
func notificationSoundChanged(filename: String, title: String) {
|
||||
switch selectedSound {
|
||||
case .Restart:
|
||||
restartSound.detailTextLabel?.text = title
|
||||
PrefsShared.RestartReminder.Sound = filename
|
||||
case .Recording:
|
||||
recordingSound.detailTextLabel?.text = title
|
||||
Prefs.RecordingReminder.Sound = filename
|
||||
if Prefs.RecordingReminder.Enabled {
|
||||
PushNotification.scheduleRecordingReminder(force: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,36 +6,63 @@ class TVCSettings: UITableViewController {
|
||||
@IBOutlet var cellDomainsIgnored: UITableViewCell!
|
||||
@IBOutlet var cellDomainsBlocked: UITableViewCell!
|
||||
@IBOutlet var cellPrivacyAutoDelete: UITableViewCell!
|
||||
@IBOutlet var cellNotificationReminder: UITableViewCell!
|
||||
@IBOutlet var cellNotificationConnectionAlert: UITableViewCell!
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
reloadToggleState()
|
||||
reloadDataSource()
|
||||
NotifyVPNStateChanged.observe(call: #selector(reloadToggleState), on: self)
|
||||
NotifyDNSFilterChanged.observe(call: #selector(reloadDataSource), on: self)
|
||||
reloadVPNState()
|
||||
reloadLoggingFilterUI()
|
||||
reloadPrivacyUI()
|
||||
NotifyVPNStateChanged.observe(call: #selector(reloadVPNState), on: self)
|
||||
NotifyDNSFilterChanged.observe(call: #selector(reloadLoggingFilterUI), on: self)
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
reloadNotificationState()
|
||||
}
|
||||
|
||||
// MARK: - VPN Proxy Settings
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
// FIXME: there is a lag between tap and open when run on device
|
||||
if let cell = tableView.cellForRow(at: indexPath), cell === cellPrivacyAutoDelete {
|
||||
openAutoDeletePicker()
|
||||
}
|
||||
}
|
||||
|
||||
func openRestartVPNSettings() { scrollToSection(0, animated: false) }
|
||||
func openNotificationSettings() { scrollToSection(2, animated: false) }
|
||||
private func scrollToSection(_ section: Int, animated: Bool) {
|
||||
tableView.scrollToRow(at: .init(row: 0, section: section), at: .top, animated: animated)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - VPN Proxy Settings
|
||||
|
||||
extension TVCSettings {
|
||||
@objc private func reloadVPNState() {
|
||||
vpnToggle.isOn = (GlassVPN.state != .off)
|
||||
vpnToggle.onTintColor = (GlassVPN.state == .inbetween ? .systemYellow : nil)
|
||||
UIApplication.shared.applicationIconBadgeNumber =
|
||||
!vpnToggle.isOn &&
|
||||
PrefsShared.RestartReminder.Enabled &&
|
||||
PrefsShared.RestartReminder.WithBadge ? 1 : 0
|
||||
}
|
||||
|
||||
@IBAction private func toggleVPNProxy(_ sender: UISwitch) {
|
||||
GlassVPN.setEnabled(sender.isOn)
|
||||
}
|
||||
|
||||
@objc private func reloadToggleState() {
|
||||
vpnToggle.isOn = (GlassVPN.state != .off)
|
||||
vpnToggle.onTintColor = (GlassVPN.state == .inbetween ? .systemYellow : nil)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Logging Filter
|
||||
|
||||
@objc private func reloadDataSource() {
|
||||
let (blocked, ignored) = DomainFilter.counts()
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Logging Filter
|
||||
|
||||
extension TVCSettings {
|
||||
@objc private func reloadLoggingFilterUI() {
|
||||
let (blocked, ignored, _, _) = DomainFilter.counts()
|
||||
cellDomainsIgnored.detailTextLabel?.text = "\(ignored) Domains"
|
||||
cellDomainsBlocked.detailTextLabel?.text = "\(blocked) Domains"
|
||||
let (one, two) = autoDeleteSelection([1, 7, 31])
|
||||
cellPrivacyAutoDelete.detailTextLabel?.text = autoDeleteString(one, unit: two)
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
|
||||
@@ -65,38 +92,84 @@ class TVCSettings: UITableViewController {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Privacy
|
||||
|
||||
extension TVCSettings {
|
||||
private func reloadPrivacyUI() {
|
||||
let (num, unit) = getAutoDeleteSelection([1, 7, 31])
|
||||
let str: String
|
||||
switch num {
|
||||
case 0: str = "Never"
|
||||
case 1: str = "1 \(["Day", "Week", "Month"][unit])"
|
||||
default: str = "\(num) \(["Days", "Weeks", "Months"][unit])"
|
||||
}
|
||||
cellPrivacyAutoDelete.detailTextLabel?.text = str
|
||||
}
|
||||
|
||||
private func getAutoDeleteSelection(_ multiplier: [Int]) -> (Int, Int) {
|
||||
let current = PrefsShared.AutoDeleteLogsDays
|
||||
let snd = multiplier.lastIndex { current % $0 == 0 }! // make sure 1 is in list
|
||||
return (current / multiplier[snd], snd)
|
||||
}
|
||||
|
||||
// MARK: - Privacy
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
// FIXME: there is a lag between tap and open when run on device
|
||||
if let cell = tableView.cellForRow(at: indexPath), cell === cellPrivacyAutoDelete {
|
||||
let multiplier = [1, 7, 31]
|
||||
let (one, two) = autoDeleteSelection(multiplier)
|
||||
|
||||
let picker = DurationPickerAlert(
|
||||
title: "Auto-delete logs",
|
||||
detail: "Warning: Logs older than the selected interval are deleted immediately! " +
|
||||
"Logs are also deleted on each app launch, and periodically in the background as long as the VPN is running.",
|
||||
options: [(0...30).map{"\($0)"}, ["Days", "Weeks", "Months"]],
|
||||
widths: [0.4, 0.6])
|
||||
picker.pickerView.setSelection([min(30, one), two])
|
||||
picker.present(in: self) { _, idx in
|
||||
cell.detailTextLabel?.text = autoDeleteString(idx[0], unit: idx[1])
|
||||
let asDays = idx[0] * multiplier[idx[1]]
|
||||
PrefsShared.AutoDeleteLogsDays = asDays
|
||||
if !GlassVPN.send(.autoDelete(after: asDays)) {
|
||||
// if VPN isn't active, fallback to immediate local delete
|
||||
TheGreatDestroyer.deleteLogs(olderThan: asDays)
|
||||
}
|
||||
private func openAutoDeletePicker() {
|
||||
let multiplier = [1, 7, 31]
|
||||
let (one, two) = getAutoDeleteSelection(multiplier)
|
||||
|
||||
let picker = DurationPickerAlert(
|
||||
title: "Auto-delete logs",
|
||||
detail: "Warning: Logs older than the selected interval are deleted immediately! " +
|
||||
"Logs are also deleted on each app launch, and periodically in the background as long as the VPN is running.",
|
||||
options: [(0...30).map{"\($0)"}, ["Days", "Weeks", "Months"]],
|
||||
widths: [0.4, 0.6])
|
||||
picker.pickerView.setSelection([min(30, one), two])
|
||||
picker.present(in: self) { _, idx in
|
||||
let asDays = idx[0] * multiplier[idx[1]]
|
||||
PrefsShared.AutoDeleteLogsDays = asDays
|
||||
self.reloadPrivacyUI()
|
||||
if !GlassVPN.send(.autoDelete(after: asDays)) {
|
||||
// if VPN isn't active, fallback to immediate local delete
|
||||
TheGreatDestroyer.deleteLogs(olderThan: asDays)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Notification Settings
|
||||
|
||||
extension TVCSettings {
|
||||
private func reloadNotificationState() {
|
||||
let lbl1 = cellNotificationReminder.detailTextLabel
|
||||
let lbl2 = cellNotificationConnectionAlert.detailTextLabel
|
||||
readNotificationState { (realAllowed, provisional) in
|
||||
lbl1?.text = provisional ? "Enabled" : "Disabled"
|
||||
lbl2?.text = realAllowed ? "Enabled" : "Disabled"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Reset Settings
|
||||
|
||||
private func readNotificationState(_ closure: @escaping (_ all: Bool, _ prov: Bool) -> Void) {
|
||||
let en1 = PrefsShared.ConnectionAlerts.Enabled
|
||||
let en2 = Prefs.RecordingReminder.Enabled || PrefsShared.RestartReminder.Enabled
|
||||
closure(en1, en2)
|
||||
guard en1 || en2 else { return }
|
||||
PushNotification.allowed { state in
|
||||
switch state {
|
||||
case .NotDetermined, .Denied: closure(false, false)
|
||||
case .Authorized: closure(en1, en2)
|
||||
case .Provisional: closure(false, en2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Reset Settings
|
||||
|
||||
extension TVCSettings {
|
||||
@IBAction private func resetTutorialAlerts(_ sender: UIButton) {
|
||||
Prefs.DidShowTutorial.Welcome = false
|
||||
Prefs.DidShowTutorial.Recordings = false
|
||||
@@ -112,10 +185,12 @@ class TVCSettings: UITableViewController {
|
||||
TheGreatDestroyer.deleteAllLogs()
|
||||
}.presentIn(self)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Advanced
|
||||
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Advanced
|
||||
|
||||
extension TVCSettings {
|
||||
@IBAction private func exportDB() {
|
||||
AppDB?.vacuum()
|
||||
let sheet = UIActivityViewController(activityItems: [URL.internalDB()], applicationActivities: nil)
|
||||
@@ -130,24 +205,3 @@ class TVCSettings: UITableViewController {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------
|
||||
// |
|
||||
// | MARK: - Helper methods
|
||||
// |
|
||||
// -------------------------------
|
||||
|
||||
private func autoDeleteSelection(_ multiplier: [Int]) -> (Int, Int) {
|
||||
let current = PrefsShared.AutoDeleteLogsDays
|
||||
let snd = multiplier.lastIndex { current % $0 == 0 }! // make sure 1 is in list
|
||||
return (current / multiplier[snd], snd)
|
||||
}
|
||||
|
||||
private func autoDeleteString(_ num: Int, unit: Int) -> String {
|
||||
switch num {
|
||||
case 0: return "Never"
|
||||
case 1: return "1 \(["Day", "Week", "Month"][unit])"
|
||||
default: return "\(num) \(["Days", "Weeks", "Months"][unit])"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@ class TBCMain: UITabBarController {
|
||||
if !Prefs.DidShowTutorial.Welcome {
|
||||
self.perform(#selector(showWelcomeMessage), with: nil, afterDelay: 0.5)
|
||||
}
|
||||
if #available(iOS 10.0, *) {
|
||||
initNotifications()
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func reloadTabBarBadge() {
|
||||
@@ -57,3 +60,63 @@ class TBCMain: UITabBarController {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension TBCMain {
|
||||
@discardableResult func openTab(_ index: Int) -> UIViewController? {
|
||||
selectedIndex = index
|
||||
guard let nav = selectedViewController as? UINavigationController else {
|
||||
return selectedViewController
|
||||
}
|
||||
nav.popToRootViewController(animated: false)
|
||||
return nav.topViewController
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Push Notifications
|
||||
|
||||
@available(iOS 10.0, *)
|
||||
extension TBCMain: UNUserNotificationCenterDelegate {
|
||||
|
||||
func initNotifications() {
|
||||
UNUserNotificationCenter.current().delegate = self
|
||||
guard Prefs.RecordingReminder.Enabled else {
|
||||
return
|
||||
}
|
||||
PushNotification.allowed {
|
||||
switch $0 {
|
||||
case .NotDetermined:
|
||||
PushNotification.requestProvisionalOrDoNothing { success in
|
||||
guard success else { return }
|
||||
PushNotification.scheduleRecordingReminder(force: false)
|
||||
}
|
||||
case .Denied:
|
||||
break
|
||||
case .Authorized, .Provisional:
|
||||
PushNotification.scheduleRecordingReminder(force: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
|
||||
completionHandler([.alert, .badge, .sound]) // in-app notifications
|
||||
}
|
||||
|
||||
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
|
||||
switch response.notification.request.identifier {
|
||||
case PushNotification.Identifier.YouShallRecordMoreReminder.rawValue:
|
||||
selectedIndex = 1 // open recordings tab
|
||||
case PushNotification.Identifier.CantStopMeNowReminder.rawValue:
|
||||
(openTab(2) as! TVCSettings).openRestartVPNSettings()
|
||||
//case PushNotification.Identifier.RestInPeaceTombstoneReminder // only badge
|
||||
default: // domain notification
|
||||
// TODO: open specific domain?
|
||||
openTab(0) // open Requests tab
|
||||
}
|
||||
completionHandler()
|
||||
}
|
||||
|
||||
@available(iOS 12.0, *)
|
||||
func userNotificationCenter(_ center: UNUserNotificationCenter, openSettingsFor notification: UNNotification?) {
|
||||
(openTab(2) as! TVCSettings).openNotificationSettings()
|
||||
}
|
||||
}
|
||||
|
||||
BIN
media/sounds/clock.caf
Normal file
BIN
media/sounds/clock.caf
Normal file
Binary file not shown.
BIN
media/sounds/drum1.caf
Normal file
BIN
media/sounds/drum1.caf
Normal file
Binary file not shown.
BIN
media/sounds/drum2.caf
Normal file
BIN
media/sounds/drum2.caf
Normal file
Binary file not shown.
BIN
media/sounds/plop1.caf
Normal file
BIN
media/sounds/plop1.caf
Normal file
Binary file not shown.
BIN
media/sounds/plop2.caf
Normal file
BIN
media/sounds/plop2.caf
Normal file
Binary file not shown.
BIN
media/sounds/snap1.caf
Normal file
BIN
media/sounds/snap1.caf
Normal file
Binary file not shown.
BIN
media/sounds/snap2.caf
Normal file
BIN
media/sounds/snap2.caf
Normal file
Binary file not shown.
BIN
media/sounds/typewriter1.caf
Normal file
BIN
media/sounds/typewriter1.caf
Normal file
Binary file not shown.
BIN
media/sounds/typewriter2.caf
Normal file
BIN
media/sounds/typewriter2.caf
Normal file
Binary file not shown.
BIN
media/sounds/wood1.caf
Normal file
BIN
media/sounds/wood1.caf
Normal file
Binary file not shown.
BIN
media/sounds/wood2.caf
Normal file
BIN
media/sounds/wood2.caf
Normal file
Binary file not shown.
Reference in New Issue
Block a user