From 5f0016490f0cd52aa8469a4ded2558a1af16bb63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Buczek?= Date: Fri, 18 Aug 2023 19:15:47 +0200 Subject: [PATCH] Fix #3939: Add Screen Time support. --- .../xcshareddata/swiftpm/Package.resolved | 2 +- Package.swift | 2 +- .../Browser/BrowserViewController.swift | 33 +++++++++++++++++++ .../BVC+ScreenTime.swift | 26 +++++++++++++++ ...serViewController+TabManagerDelegate.swift | 1 + .../Brave/Frontend/ClientPreferences.swift | 3 ++ .../OtherPrivacySettingsSectionView.swift | 5 +++ Sources/BraveShared/BraveURLs.swift | 2 ++ Sources/BraveStrings/BraveStrings.swift | 2 ++ 9 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 Sources/Brave/Frontend/Browser/BrowserViewController/BVC+ScreenTime.swift diff --git a/App/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/App/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index c335bdb8c8c..13009cda388 100644 --- a/App/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/App/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -41,7 +41,7 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/brave/PanModal", "state" : { - "revision" : "e4c07f8e6c5df937051fabc47e1e92901e1d068b" + "revision" : "e67e9eff53c05f19b41bbb2ca7d27ff5859a586c" } }, { diff --git a/Package.swift b/Package.swift index 175980d2ce2..87a0d6ca0f2 100644 --- a/Package.swift +++ b/Package.swift @@ -51,7 +51,7 @@ var package = Package( .package(url: "https://github.com/SDWebImage/SDWebImage", exact: "5.10.3"), .package(url: "https://github.com/SDWebImage/SDWebImageSwiftUI", from: "2.2.0"), .package(url: "https://github.com/nmdias/FeedKit", from: "9.1.2"), - .package(url: "https://github.com/brave/PanModal", revision: "e4c07f8e6c5df937051fabc47e1e92901e1d068b"), + .package(url: "https://github.com/brave/PanModal", revision: "e67e9eff53c05f19b41bbb2ca7d27ff5859a586c"), .package(url: "https://github.com/apple/swift-collections", from: "1.0.0"), .package(url: "https://github.com/siteline/SwiftUI-Introspect", from: "0.1.3"), .package(url: "https://github.com/apple/swift-algorithms", from: "1.0.0"), diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController.swift b/Sources/Brave/Frontend/Browser/BrowserViewController.swift index 190437c7000..37e8653336a 100644 --- a/Sources/Brave/Frontend/Browser/BrowserViewController.swift +++ b/Sources/Brave/Frontend/Browser/BrowserViewController.swift @@ -30,6 +30,7 @@ import Onboarding import Growth import BraveShields import CertificateUtilities +import ScreenTime private let KVOs: [KVOConstants] = [ .estimatedProgress, @@ -367,6 +368,10 @@ public class BrowserViewController: UIViewController { self.braveCore.syncAPI.leaveSyncGroup() } } + + if Preferences.Privacy.screenTimeEnabled.value { + screenTimeViewController = STWebpageController() + } } deinit { @@ -446,6 +451,7 @@ public class BrowserViewController: UIViewController { Preferences.Playlist.syncSharedFoldersAutomatically.observe(from: self) Preferences.NewTabPage.backgroundSponsoredImages.observe(from: self) ShieldPreferences.blockAdsAndTrackingLevelRaw.observe(from: self) + Preferences.Privacy.screenTimeEnabled.observe(from: self) pageZoomListener = NotificationCenter.default.addObserver(forName: PageZoomView.notificationName, object: nil, queue: .main) { [weak self] _ in self?.tabManager.allTabs.forEach({ @@ -640,6 +646,7 @@ public class BrowserViewController: UIViewController { func updateToolbarCurrentURL(_ currentURL: URL?) { topToolbar.currentURL = currentURL collapsedURLBarView.currentURL = currentURL + updateScreenTimeUrl(currentURL) } override public func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) { @@ -1168,6 +1175,9 @@ public class BrowserViewController: UIViewController { /// A layout guide defining where the favorites and NTP overlay are placed let pageOverlayLayoutGuide = UILayoutGuide() + + /// A single controller per bvc/window. Using one controller per tab or webview causes crashes. + var screenTimeViewController: STWebpageController? override public func updateViewConstraints() { readerModeBar?.snp.remakeConstraints { make in @@ -1180,6 +1190,16 @@ public class BrowserViewController: UIViewController { make.leading.trailing.equalTo(self.view) } + if let screenTimeViewController = screenTimeViewController { + webViewContainer.addSubview(screenTimeViewController.view) + addChild(screenTimeViewController) + screenTimeViewController.didMove(toParent: self) + + screenTimeViewController.view.snp.remakeConstraints { + $0.edges.equalTo(webViewContainer) + } + } + webViewContainer.snp.remakeConstraints { make in make.left.right.equalTo(self.view) @@ -3153,6 +3173,19 @@ extension BrowserViewController: PreferencesObserver { syncPlaylistFolders() case Preferences.NewTabPage.backgroundSponsoredImages.key: recordAdsUsageType() + case Preferences.Privacy.screenTimeEnabled.key: + if Preferences.Privacy.screenTimeEnabled.value { + screenTimeViewController = STWebpageController() + if let tab = tabManager.selectedTab { + recordScreentimeUsage(for: tab) + } + updateViewConstraints() + } else { + screenTimeViewController?.view.removeFromSuperview() + screenTimeViewController?.removeFromParent() + screenTimeViewController?.suppressUsageRecording = true + screenTimeViewController = nil + } default: Logger.module.debug("Received a preference change for an unknown key: \(key, privacy: .public) on \(type(of: self), privacy: .public)") break diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+ScreenTime.swift b/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+ScreenTime.swift new file mode 100644 index 00000000000..5113aa53da1 --- /dev/null +++ b/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+ScreenTime.swift @@ -0,0 +1,26 @@ +// Copyright 2023 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import Foundation +import Preferences + +extension BrowserViewController { + /// Updates the url of the screen time controller. Keep in mind a single screen time controller is used for every window. + /// Multiple instances of it seem to crash the app. + /// There is also one hack required: STWebpageController breaks if you pass scheme other than http or https, + /// it will not block anything for the rest of its lifecycle. Our internal urls have to be bridged to an empty https url. + func updateScreenTimeUrl(_ url: URL?) { + guard let url = url, (url.scheme == "http" || url.scheme == "https") else { + screenTimeViewController?.url = URL(string: "https://about:blank") + return + } + + screenTimeViewController?.url = url + } + + func recordScreentimeUsage(for tab: Tab) { + screenTimeViewController?.suppressUsageRecording = tab.isPrivate || !Preferences.Privacy.screenTimeEnabled.value + } +} diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+TabManagerDelegate.swift b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+TabManagerDelegate.swift index 516bcdf0cab..7c195598833 100644 --- a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+TabManagerDelegate.swift +++ b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+TabManagerDelegate.swift @@ -42,6 +42,7 @@ extension BrowserViewController: TabManagerDelegate { }) updateURLBar() + recordScreentimeUsage(for: tab) if let url = tab.url, !InternalURL.isValid(url: url) { let previousEstimatedProgress = previous?.webView?.estimatedProgress ?? 1.0 diff --git a/Sources/Brave/Frontend/ClientPreferences.swift b/Sources/Brave/Frontend/ClientPreferences.swift index 2a5d8d3fd99..0ad5f569815 100644 --- a/Sources/Brave/Frontend/ClientPreferences.swift +++ b/Sources/Brave/Frontend/ClientPreferences.swift @@ -132,6 +132,9 @@ extension Preferences { static let blockAllCookies = Option(key: "privacy.block-all-cookies", default: false) /// The toggles states for clear private data screen static let clearPrivateDataToggles = Option<[Bool]>(key: "privacy.clear-data-toggles", default: []) + /// Enables the Apple's Screen Time feature. + public static let screenTimeEnabled = Option(key: "privacy.screentime", default: true) + } final public class NewTabPage { /// Whether bookmark image are enabled / shown diff --git a/Sources/Brave/Frontend/Settings/Features/ShieldsPrivacy/OtherPrivacySettingsSectionView.swift b/Sources/Brave/Frontend/Settings/Features/ShieldsPrivacy/OtherPrivacySettingsSectionView.swift index c62a14313fb..201ae5e93fa 100644 --- a/Sources/Brave/Frontend/Settings/Features/ShieldsPrivacy/OtherPrivacySettingsSectionView.swift +++ b/Sources/Brave/Frontend/Settings/Features/ShieldsPrivacy/OtherPrivacySettingsSectionView.swift @@ -69,6 +69,11 @@ struct OtherPrivacySettingsSectionView: View { subtitle: String.localizedStringWithFormat(Strings.googleSafeBrowsingUsingWebKitDescription, URL.brave.safeBrowsingHelp.absoluteString), option: Preferences.Shields.googleSafeBrowsing ) + OptionToggleView( + title: Strings.screenTimeSetting, + subtitle: String.localizedStringWithFormat(Strings.screenTimeSettingDescription, URL.brave.screenTimeHelp.absoluteString), + option: Preferences.Privacy.screenTimeEnabled + ) ShieldToggleView( title: Strings.P3A.settingTitle, subtitle: Strings.P3A.settingSubtitle, diff --git a/Sources/BraveShared/BraveURLs.swift b/Sources/BraveShared/BraveURLs.swift index 8ef5fd6b32a..d23857e61ae 100644 --- a/Sources/BraveShared/BraveURLs.swift +++ b/Sources/BraveShared/BraveURLs.swift @@ -26,6 +26,8 @@ extension URL { public static let braveVPNLinkReceiptDev = URL(string: "https://account.brave.software/?intent=connect-receipt&product=vpn")! public static let safeBrowsingHelp = URL(string: "https://support.brave.com/hc/en-us/articles/15222663599629-Safe-Browsing-in-Brave")! + public static let screenTimeHelp = + URL(string: "https://support.apple.com/guide/security/secd8831e732/web")! } public static let brave = Brave.self } diff --git a/Sources/BraveStrings/BraveStrings.swift b/Sources/BraveStrings/BraveStrings.swift index 2bdaeef60b7..be8443f92f2 100644 --- a/Sources/BraveStrings/BraveStrings.swift +++ b/Sources/BraveStrings/BraveStrings.swift @@ -1112,6 +1112,8 @@ extension Strings { public static let HTTPSEverywhereDescription = NSLocalizedString("HTTPSEverywhereDescription", tableName: "BraveShared", bundle: .module, value: "Opens sites using secure HTTPS instead of HTTP when possible.", comment: "") public static let googleSafeBrowsing = NSLocalizedString("GoogleSafeBrowsing", tableName: "BraveShared", bundle: .module, value: "Block Dangerous Websites", comment: "") public static let googleSafeBrowsingUsingWebKitDescription = NSLocalizedString("GoogleSafeBrowsingUsingWebKitDescription", tableName: "BraveShared", bundle: .module, value: "Safe Browsing protects against websites which are known to be dangerous. [Learn More](%@)", comment: "") + public static let screenTimeSetting = NSLocalizedString("settings.privacy.screenTimeSetting", tableName: "BraveShared", bundle: .module, value: "Enable Screen Time", comment: "") + public static let screenTimeSettingDescription = NSLocalizedString("settings.privacy.screenTimeSettingDescription", tableName: "BraveShared", bundle: .module, value: "Allows you to record what websites you spend time on and set web browsing limits. [Learn More](%@)", comment: "") public static let blockScripts = NSLocalizedString("BlockScripts", tableName: "BraveShared", bundle: .module, value: "Block Scripts", comment: "") public static let blockScriptsDescription = NSLocalizedString("BlockScriptsDescription", tableName: "BraveShared", bundle: .module, value: "Blocks JavaScript (may break sites).", comment: "") public static let blockCookiesDescription = NSLocalizedString("BlockCookiesDescription", tableName: "BraveShared", bundle: .module, value: "Prevents websites from storing information about your previous visits.", comment: "")