diff --git a/PIA VPN.xcodeproj/project.pbxproj b/PIA VPN.xcodeproj/project.pbxproj index 22bfed95..4e2a1250 100644 --- a/PIA VPN.xcodeproj/project.pbxproj +++ b/PIA VPN.xcodeproj/project.pbxproj @@ -715,6 +715,10 @@ E53627372BE802200064DEDF /* SignupUseCaseMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E53627362BE802200064DEDF /* SignupUseCaseMock.swift */; }; E548E1FF2C1238BD001E9874 /* SnapshotTesting in Frameworks */ = {isa = PBXBuildFile; productRef = E548E1FE2C1238BD001E9874 /* SnapshotTesting */; }; E548E2002C1238CD001E9874 /* WelcomeViewSnapshotTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = E548E1EC2C123782001E9874 /* WelcomeViewSnapshotTest.swift */; }; + E55188932CB66BCA00E4C241 /* ForceUpdateViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E55188912CB66BCA00E4C241 /* ForceUpdateViewController.swift */; }; + E55188942CB66BCA00E4C241 /* ForceUpdateViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = E55188922CB66BCA00E4C241 /* ForceUpdateViewController.xib */; }; + E55188952CB66BCA00E4C241 /* ForceUpdateViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E55188912CB66BCA00E4C241 /* ForceUpdateViewController.swift */; }; + E55188962CB66BCA00E4C241 /* ForceUpdateViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = E55188922CB66BCA00E4C241 /* ForceUpdateViewController.xib */; }; E55216342BEF63EA001A287F /* SignupIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E55216332BEF63EA001A287F /* SignupIntegrationTests.swift */; }; E55216362BF1393C001A287F /* SignupEmailIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E55216352BF1393C001A287F /* SignupEmailIntegrationTests.swift */; }; E55216382BF14A5B001A287F /* SignupDomainErrorMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = E55216372BF14A5B001A287F /* SignupDomainErrorMapper.swift */; }; @@ -1685,6 +1689,8 @@ E548E1EC2C123782001E9874 /* WelcomeViewSnapshotTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeViewSnapshotTest.swift; sourceTree = ""; }; E548E1F52C1238B5001E9874 /* PIA VPN-tvOS snapshot.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "PIA VPN-tvOS snapshot.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; E548E2012C124AA0001E9874 /* PIA VPN-tvOS snapshot.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = "PIA VPN-tvOS snapshot.xctestplan"; sourceTree = ""; }; + E55188912CB66BCA00E4C241 /* ForceUpdateViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForceUpdateViewController.swift; sourceTree = ""; }; + E55188922CB66BCA00E4C241 /* ForceUpdateViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ForceUpdateViewController.xib; sourceTree = ""; }; E55216332BEF63EA001A287F /* SignupIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignupIntegrationTests.swift; sourceTree = ""; }; E55216352BF1393C001A287F /* SignupEmailIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignupEmailIntegrationTests.swift; sourceTree = ""; }; E55216372BF14A5B001A287F /* SignupDomainErrorMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignupDomainErrorMapper.swift; sourceTree = ""; }; @@ -2373,6 +2379,7 @@ 296BBFEE1840066A00944151 /* UI */ = { isa = PBXGroup; children = ( + E55188902CB66B7900E4C241 /* Force update */, E5217F5C2AEAE01400123442 /* NotificationCategory.swift */, E5217F532AEAD54600123442 /* WalkthroughPageView.swift */, E5217F4A2AEAD29F00123442 /* PurchasePlanCell.swift */, @@ -3567,6 +3574,15 @@ path = "PIA VPNTests snapshot"; sourceTree = ""; }; + E55188902CB66B7900E4C241 /* Force update */ = { + isa = PBXGroup; + children = ( + E55188912CB66BCA00E4C241 /* ForceUpdateViewController.swift */, + E55188922CB66BCA00E4C241 /* ForceUpdateViewController.xib */, + ); + name = "Force update"; + sourceTree = ""; + }; E55216322BEF63AD001A287F /* Integration */ = { isa = PBXGroup; children = ( @@ -5007,6 +5023,7 @@ 0ED66BD020A9918000333B35 /* staging.endpoint in Resources */, DD76292021ECCD510092DF50 /* UsageTileCollectionViewCell.xib in Resources */, E501CBB72AE97EC800515006 /* Welcome.storyboard in Resources */, + E55188962CB66BCA00E4C241 /* ForceUpdateViewController.xib in Resources */, DD6DC5C321B6C27F00F9D538 /* pia-spinner.json in Resources */, 0E0715E7201CBB7100D6F666 /* Flags-dev.plist in Resources */, DD1C138A21E60C63004004B3 /* IPTile.xib in Resources */, @@ -5103,6 +5120,7 @@ 291C6398183EBC210039EC03 /* Images.xcassets in Resources */, 82183D9625014FDC0033023F /* NetworkCollectionViewCell.xib in Resources */, 82183D9825014FDC0033023F /* CustomNetworkCollectionViewCell.xib in Resources */, + E55188942CB66BCA00E4C241 /* ForceUpdateViewController.xib in Resources */, E501CBF72AE9806800515006 /* Signup.strings in Resources */, 0E0786DE1EFA7EAE00F77466 /* Components.plist in Resources */, E501CBB32AE97EBA00515006 /* Signup.storyboard in Resources */, @@ -5422,6 +5440,7 @@ E5C507702B0DEC2400200A6A /* AutolayoutViewController.swift in Sources */, 82CAB8F3255C0CD100BB08EF /* MessagesTileCollectionViewCell.swift in Sources */, E5217F462AEACE0200123442 /* NavigationLogoView.swift in Sources */, + E55188952CB66BCA00E4C241 /* ForceUpdateViewController.swift in Sources */, E58A45622BA8E373002A0704 /* LoginProvider.swift in Sources */, E59E8FB02AEA7A81009278F5 /* MagicLinkLoginViewController.swift in Sources */, 0E3A352D1FD9CDC5000B0F99 /* Theme+App.swift in Sources */, @@ -5516,6 +5535,7 @@ 8272C6332657EE4E00D846A8 /* AutomationSettingsViewController.swift in Sources */, E59E8FBB2AEA7A81009278F5 /* GDPRViewController.swift in Sources */, DDF7F755240D35BA00A671C7 /* CustomServerSettingsViewController.swift in Sources */, + E55188932CB66BCA00E4C241 /* ForceUpdateViewController.swift in Sources */, 0ECF5C082017EBAD0047596C /* ThemeCode.swift in Sources */, 3545E98026AAD60C00B812CC /* ServerSelectionDelegate.swift in Sources */, E5217F402AEAC7A900123442 /* Theme+LightPalette.swift in Sources */, diff --git a/PIA VPN/AppDelegate.swift b/PIA VPN/AppDelegate.swift index b6b37d15..e83248c4 100644 --- a/PIA VPN/AppDelegate.swift +++ b/PIA VPN/AppDelegate.swift @@ -230,7 +230,6 @@ class AppDelegate: NSObject, UIApplicationDelegate { let accountInformationVerifier = AccountInformationAvailabilityFactory.makeAccountInformationAvailabilityVerifier() accountInformationVerifier.verifyAccountInformationAvailabity(after: AccountInformationAvailabilityVerifier.defaultDeadlineInSeconds, completion: nil) - } private func refreshShortcutItems(in application: UIApplication) { diff --git a/PIA VPN/Bootstrapper.swift b/PIA VPN/Bootstrapper.swift index a4ae51ff..39c7978c 100644 --- a/PIA VPN/Bootstrapper.swift +++ b/PIA VPN/Bootstrapper.swift @@ -28,7 +28,11 @@ import TunnelKitOpenVPN import PIAWireguard #endif import SwiftyBeaver +import UIKit +extension NSNotification.Name { + public static let __AppDidFetchForceUpdateFeatureFlag = Notification.Name("__AppDidFetchForceUpdateFeatureFlag") +} class Bootstrapper { @@ -105,7 +109,8 @@ class Bootstrapper { Client.configuration.rsa4096Certificate = rsa4096Certificate() #if PIA_DEV - Client.environment = AppPreferences.shared.appEnvironmentIsProduction ? .production : .staging + //Client.environment = AppPreferences.shared.appEnvironmentIsProduction ? .production : .staging + Client.environment = .staging #else Client.environment = AppConfiguration.clientEnvironment #endif @@ -163,11 +168,11 @@ class Bootstrapper { AppPreferences.shared.disablesMultiDipTokens = Client.configuration.featureFlags.contains(Client.FeatureFlags.disableMultiDipTokens) AppPreferences.shared.showNewInitialScreen = Client.configuration.featureFlags.contains(Client.FeatureFlags.showNewInitialScreen) - /// Updates the feature flags values to the ones set on the server only on Release builds. /// (like Leak protection feature) self.updateFeatureFlagsForReleaseIfNeeded() + self.checkForceUpdateIfNeeded() }) //FORCE THE MIGRATION TO GEN4 @@ -327,3 +332,11 @@ class Bootstrapper { #endif } } + +extension Bootstrapper { + func checkForceUpdateIfNeeded() { + if Client.configuration.featureFlags.contains("force_update") { + NotificationCenter.default.post(name: Notification.Name.__AppDidFetchForceUpdateFeatureFlag, object: nil) + } + } +} diff --git a/PIA VPN/DashboardViewController.swift b/PIA VPN/DashboardViewController.swift index 63350f74..15dbf182 100644 --- a/PIA VPN/DashboardViewController.swift +++ b/PIA VPN/DashboardViewController.swift @@ -133,6 +133,7 @@ class DashboardViewController: AutolayoutViewController { nc.addObserver(self, selector: #selector(connectionVPNStatusDidChange(_:)), name: NSNotification.Name.NEVPNStatusDidChange, object: nil) nc.addObserver(self, selector: #selector(handleDidConnectToRFC1918CompliantWifi(_:)), name: NSNotification.Name.DeviceDidConnectToRFC1918CompliantWifi, object: nil) nc.addObserver(self, selector: #selector(checkConnectToRFC1918VulnerableWifi(_:)), name: NSNotification.Name.DeviceDidConnectToRFC1918VulnerableWifi, object: nil) + nc.addObserver(self, selector: #selector(presentForceUpdate), name: NSNotification.Name.__AppDidFetchForceUpdateFeatureFlag, object: nil) self.viewContentHeight = self.viewContentHeightConstraint.constant } @@ -701,6 +702,14 @@ class DashboardViewController: AutolayoutViewController { } } + @objc func presentForceUpdate() { + let forceUpdate = ForceUpdateViewController() + forceUpdate.modalPresentationStyle = .fullScreen + DispatchQueue.main.async { [weak self] in + self?.present(forceUpdate, animated: false) + } + } + //MARK: Non compliant Wifi alert private struct WifiAlertAction { diff --git a/PIA VPN/ForceUpdateViewController.swift b/PIA VPN/ForceUpdateViewController.swift new file mode 100644 index 00000000..f9d9ba30 --- /dev/null +++ b/PIA VPN/ForceUpdateViewController.swift @@ -0,0 +1,40 @@ +// +// ForceUpdateViewController.swift +// PIA VPN +// +// Created by Said Rehouni on 9/10/24. +// Copyright © 2024 Private Internet Access Inc. All rights reserved. +// + +import UIKit +import PIALibrary + +class ForceUpdateViewController: UIViewController { + @IBOutlet weak var updateButton: PIAButton! + @IBOutlet weak var titleLabel: UILabel! + @IBOutlet weak var descriptionLabel: UILabel! + + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + + styleUpdateButton() + + updateButton.addTarget(self, action: #selector(updateButtonTapped), for: .touchUpInside) + updateButton.setTitle("Update Now", for: .normal) + } + + @objc func updateButtonTapped() { + Client.providers.vpnProvider.uninstallAll() + UIApplication.shared.open(URL(string: "https://itunes.apple.com/app/private-internet-access-anonymous/id955626407")!) + } + + private func styleUpdateButton() { + updateButton.setRounded() + updateButton.style(style: TextStyle.Buttons.piaGreenButton) + updateButton.setTitle(L10n.Welcome.Purchase.submit.uppercased(), + for: []) + } +} diff --git a/PIA VPN/ForceUpdateViewController.xib b/PIA VPN/ForceUpdateViewController.xib new file mode 100644 index 00000000..c4eb2520 --- /dev/null +++ b/PIA VPN/ForceUpdateViewController.xib @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PIA VPN/Images.xcassets/force_update_shield.imageset/Contents.json b/PIA VPN/Images.xcassets/force_update_shield.imageset/Contents.json new file mode 100644 index 00000000..379b270c --- /dev/null +++ b/PIA VPN/Images.xcassets/force_update_shield.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "shield.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/PIA VPN/Images.xcassets/force_update_shield.imageset/shield.pdf b/PIA VPN/Images.xcassets/force_update_shield.imageset/shield.pdf new file mode 100644 index 00000000..e775919c Binary files /dev/null and b/PIA VPN/Images.xcassets/force_update_shield.imageset/shield.pdf differ diff --git a/PIA VPN/PIAHotspotHelper.swift b/PIA VPN/PIAHotspotHelper.swift index 9d6eee4b..22e54517 100644 --- a/PIA VPN/PIAHotspotHelper.swift +++ b/PIA VPN/PIAHotspotHelper.swift @@ -71,6 +71,9 @@ class PIAHotspotHelper { } return NEHotspotHelper.register(options: options, queue: DispatchQueue.main) { [weak self] (cmd: NEHotspotHelperCommand) in + + guard !Client.configuration.featureFlags.contains("force_update") else { return } + if let weakSelf = self { if cmd.commandType == .filterScanList { log.info("filtering ssid list") diff --git a/PIA VPN/PIAWelcomeViewController.swift b/PIA VPN/PIAWelcomeViewController.swift index 4d93cfbe..b346d82d 100644 --- a/PIA VPN/PIAWelcomeViewController.swift +++ b/PIA VPN/PIAWelcomeViewController.swift @@ -83,8 +83,8 @@ public class PIAWelcomeViewController: AutolayoutViewController, WelcomeCompleti #if os(iOS) let nc = NotificationCenter.default nc.addObserver(self, selector: #selector(inAppDidAddUncredited(notification:)), name: .__InAppDidAddUncredited, object: nil) + nc.addObserver(self, selector: #selector(presentForceUpdate(notification:)), name: .__AppDidFetchForceUpdateFeatureFlag, object: nil) #endif - } /// :nodoc: @@ -204,6 +204,14 @@ public class PIAWelcomeViewController: AutolayoutViewController, WelcomeCompleti } #endif + @objc func presentForceUpdate(notification: Notification) { + let forceUpdate = ForceUpdateViewController() + forceUpdate.modalPresentationStyle = .fullScreen + DispatchQueue.main.async { [weak self] in + self?.present(forceUpdate, animated: false) + } + } + // MARK: Size classes // consider compact height in landscape