Add screenshot generator
This commit is contained in:
@@ -91,6 +91,15 @@ public class CommandClient: ObservableObject {
|
||||
self.init([connectionType], logMaxLines: logMaxLines)
|
||||
}
|
||||
|
||||
public func setupMockData() {
|
||||
isConnected = true
|
||||
clashModeList = ["rule", "global", "direct"]
|
||||
clashMode = "rule"
|
||||
uplinkHistory = Array(repeating: CGFloat(1000), count: 30)
|
||||
downlinkHistory = Array(repeating: CGFloat(5000), count: 30)
|
||||
hasAnyConnection = true
|
||||
}
|
||||
|
||||
public func connect() {
|
||||
if isConnected {
|
||||
return
|
||||
|
||||
@@ -141,7 +141,13 @@ public class ExtensionEnvironments: ObservableObject {
|
||||
public let selectedProfileUpdate = ObjectWillChangePublisher()
|
||||
public let openSettings = ObjectWillChangePublisher()
|
||||
|
||||
public init() {}
|
||||
public init() {
|
||||
if Variant.screenshotMode {
|
||||
extensionProfileLoading = false
|
||||
extensionProfile = .mock
|
||||
commandClient.setupMockData()
|
||||
}
|
||||
}
|
||||
|
||||
public func postReload() {
|
||||
Task {
|
||||
@@ -150,6 +156,7 @@ public class ExtensionEnvironments: ObservableObject {
|
||||
}
|
||||
|
||||
public func reload() async {
|
||||
if Variant.screenshotMode { return }
|
||||
if let newProfile = try? await ExtensionProfile.load() {
|
||||
if extensionProfile == nil || extensionProfile?.status == .invalid {
|
||||
newProfile.register()
|
||||
@@ -163,6 +170,7 @@ public class ExtensionEnvironments: ObservableObject {
|
||||
}
|
||||
|
||||
public func connect() {
|
||||
if Variant.screenshotMode { return }
|
||||
guard let profile = extensionProfile else {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -12,9 +12,10 @@ private let logger = Logger(category: "ExtensionProfile")
|
||||
public class ExtensionProfile: ObservableObject {
|
||||
public static let controlKind = AppConfiguration.widgetControlKind
|
||||
|
||||
private let manager: NEVPNManager
|
||||
private var connection: NEVPNConnection
|
||||
private let manager: NEVPNManager?
|
||||
private var connection: NEVPNConnection?
|
||||
private var observer: Any?
|
||||
private let isMock: Bool
|
||||
|
||||
@Published public var status: NEVPNStatus
|
||||
@Published public var connectedDate: Date?
|
||||
@@ -24,9 +25,28 @@ public class ExtensionProfile: ObservableObject {
|
||||
connection = manager.connection
|
||||
status = manager.connection.status
|
||||
connectedDate = manager.connection.connectedDate
|
||||
isMock = false
|
||||
}
|
||||
|
||||
private init(mockStatus: NEVPNStatus, mockConnectedDate: Date?) {
|
||||
manager = nil
|
||||
connection = nil
|
||||
status = mockStatus
|
||||
connectedDate = mockConnectedDate
|
||||
isMock = true
|
||||
}
|
||||
|
||||
private static var _mock: ExtensionProfile?
|
||||
|
||||
public static var mock: ExtensionProfile {
|
||||
if _mock == nil {
|
||||
_mock = ExtensionProfile(mockStatus: .connected, mockConnectedDate: Date().addingTimeInterval(-3600))
|
||||
}
|
||||
return _mock!
|
||||
}
|
||||
|
||||
public func register() {
|
||||
guard !isMock, let manager else { return }
|
||||
observer = NotificationCenter.default.addObserver(
|
||||
forName: NSNotification.Name.NEVPNStatusDidChange,
|
||||
object: manager.connection,
|
||||
@@ -88,6 +108,7 @@ public class ExtensionProfile: ObservableObject {
|
||||
}
|
||||
|
||||
private func setOnDemandRules(useDefaultRules: Bool) async {
|
||||
guard let manager else { return }
|
||||
if useDefaultRules {
|
||||
manager.onDemandRules = Self.makeDefaultOnDemandRules()
|
||||
} else {
|
||||
@@ -97,6 +118,7 @@ public class ExtensionProfile: ObservableObject {
|
||||
}
|
||||
|
||||
public func updateOnDemand(enabled: Bool, useDefaultRules: Bool) async throws {
|
||||
guard let manager else { return }
|
||||
manager.isOnDemandEnabled = enabled
|
||||
await setOnDemandRules(useDefaultRules: useDefaultRules)
|
||||
try await manager.saveToPreferences()
|
||||
@@ -104,10 +126,19 @@ public class ExtensionProfile: ObservableObject {
|
||||
|
||||
@available(iOS 16.0, macOS 13.0, tvOS 17.0, *)
|
||||
public func fetchLastDisconnectError() async throws {
|
||||
guard let connection else { return }
|
||||
try await connection.fetchLastDisconnectError()
|
||||
}
|
||||
|
||||
public func start() async throws {
|
||||
if isMock {
|
||||
status = .connecting
|
||||
try await Task.sleep(nanoseconds: 500_000_000)
|
||||
status = .connected
|
||||
connectedDate = Date()
|
||||
return
|
||||
}
|
||||
guard let manager else { return }
|
||||
try await fetchProfile()
|
||||
manager.isEnabled = true
|
||||
let alwaysOn = await SharedPreferences.alwaysOn.get()
|
||||
@@ -131,6 +162,7 @@ public class ExtensionProfile: ObservableObject {
|
||||
}
|
||||
|
||||
public func reloadService() async throws {
|
||||
if isMock { return }
|
||||
let options = try await prepareStartOptions()
|
||||
let data = try ExtensionStartOptions.encode(options)
|
||||
guard let session = connection as? NETunnelProviderSession else {
|
||||
@@ -197,6 +229,14 @@ public class ExtensionProfile: ObservableObject {
|
||||
}
|
||||
|
||||
public func stop() async throws {
|
||||
if isMock {
|
||||
status = .disconnecting
|
||||
try await Task.sleep(nanoseconds: 300_000_000)
|
||||
status = .disconnected
|
||||
connectedDate = nil
|
||||
return
|
||||
}
|
||||
guard let manager else { return }
|
||||
if manager.isOnDemandEnabled {
|
||||
manager.isOnDemandEnabled = false
|
||||
try await manager.saveToPreferences()
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import Foundation
|
||||
import ObjectiveC
|
||||
|
||||
private var screenshotLocalizationBundleKey: UInt8 = 0
|
||||
|
||||
private final class ScreenshotBundle: Bundle, @unchecked Sendable {
|
||||
override func localizedString(forKey key: String, value: String?, table tableName: String?) -> String {
|
||||
if let bundle = objc_getAssociatedObject(self, &screenshotLocalizationBundleKey) as? Bundle {
|
||||
return bundle.localizedString(forKey: key, value: value, table: tableName)
|
||||
}
|
||||
return super.localizedString(forKey: key, value: value, table: tableName)
|
||||
}
|
||||
}
|
||||
|
||||
public enum ScreenshotLocalization {
|
||||
public static func applyIfNeeded() {
|
||||
let environment = ProcessInfo.processInfo.environment
|
||||
guard let language = environment["SCREENSHOT_LANGUAGE"]?
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||
!language.isEmpty
|
||||
else {
|
||||
return
|
||||
}
|
||||
UserDefaults.standard.set([language], forKey: "AppleLanguages")
|
||||
if let locale = environment["SCREENSHOT_LOCALE"]?
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||
!locale.isEmpty
|
||||
{
|
||||
UserDefaults.standard.set(locale, forKey: "AppleLocale")
|
||||
}
|
||||
if let path = Bundle.main.path(forResource: language, ofType: "lproj"),
|
||||
let localizedBundle = Bundle(path: path)
|
||||
{
|
||||
objc_setAssociatedObject(Bundle.main, &screenshotLocalizationBundleKey, localizedBundle, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
object_setClass(Bundle.main, ScreenshotBundle.self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private let _screenshotLocalizationApplied: Void = {
|
||||
ScreenshotLocalization.applyIfNeeded()
|
||||
}()
|
||||
@@ -22,4 +22,10 @@ public enum Variant {
|
||||
public static var debugNoIOS26 = false
|
||||
public static var debugNoIOS18 = false
|
||||
#endif
|
||||
|
||||
#if targetEnvironment(simulator)
|
||||
public static let screenshotMode = true
|
||||
#else
|
||||
public static let screenshotMode = ProcessInfo.processInfo.arguments.contains("-FASTLANE_SNAPSHOT")
|
||||
#endif
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user