feat: Brings a new transparent glass material background

- fix: Fixed the issue where the 182/107 error code appeared during installation on some models and scenarios
This commit is contained in:
X1a0He
2025-03-27 01:05:20 +08:00
parent 5b5de8900e
commit f823984b7f
26 changed files with 467 additions and 2073 deletions

View File

@@ -31,7 +31,7 @@
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Release"
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"

View File

@@ -32,7 +32,7 @@
endingColumnNumber = "9223372036854775807"
startingLineNumber = "81"
endingLineNumber = "81"
landmarkName = "executeInstallation(at:progressHandler:)"
landmarkName = "getAdobeInstallLogDetails()"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>

View File

@@ -57,43 +57,48 @@ struct Adobe_DownloaderApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(globalNetworkManager)
.frame(minWidth: 792, minHeight: 600)
.tint(.blue)
.task {
await setupApplication()
}
.sheet(isPresented: $showCreativeCloudAlert) {
ShouldExistsSetUpView()
.environmentObject(globalNetworkManager)
}
.alert("Setup未备份提示", isPresented: $showBackupAlert) {
Button("确定") {
handleBackup()
}
Button("取消", role: .cancel) {}
} message: {
Text("检测到Setup文件尚未备份如果你需要安装程序则Setup必须被处理点击确定后你需要输入密码Adobe Downloader将自动处理并备份为Setup.original")
}
.alert(backupSuccess ? "备份成功" : "备份失败", isPresented: $showBackupResultAlert) {
Button("确定") { }
} message: {
Text(backupResultMessage)
}
.sheet(isPresented: $showTipsSheet) {
TipsSheetView(
showTipsSheet: $showTipsSheet,
showLanguagePicker: $showLanguagePicker
)
ZStack {
BlurView()
.ignoresSafeArea()
ContentView()
.environmentObject(globalNetworkManager)
.sheet(isPresented: $showLanguagePicker) {
LanguagePickerView(languages: AppStatics.supportedLanguages) { language in
storage.defaultLanguage = language
showLanguagePicker = false
.frame(minWidth: 792, minHeight: 600)
.tint(.blue)
.task {
await setupApplication()
}
.sheet(isPresented: $showCreativeCloudAlert) {
ShouldExistsSetUpView()
.environmentObject(globalNetworkManager)
}
.alert("Setup未备份提示", isPresented: $showBackupAlert) {
Button("确定") {
handleBackup()
}
Button("取消", role: .cancel) {}
} message: {
Text("检测到Setup文件尚未备份如果你需要安装程序则Setup必须被处理点击确定后你需要输入密码Adobe Downloader将自动处理并备份为Setup.original")
}
.alert(backupSuccess ? "备份成功" : "备份失败", isPresented: $showBackupResultAlert) {
Button("确定") { }
} message: {
Text(backupResultMessage)
}
.sheet(isPresented: $showTipsSheet) {
TipsSheetView(
showTipsSheet: $showTipsSheet,
showLanguagePicker: $showLanguagePicker
)
.environmentObject(globalNetworkManager)
.sheet(isPresented: $showLanguagePicker) {
LanguagePickerView(languages: AppStatics.supportedLanguages) { language in
storage.defaultLanguage = language
showLanguagePicker = false
}
}
}
}
}
}
.windowStyle(.hiddenTitleBar)
.windowResizabilityContentSize()

View File

@@ -1,12 +1,40 @@
import Cocoa
import SwiftUI
struct BlurView: NSViewRepresentable {
func makeNSView(context: Context) -> NSVisualEffectView {
let effectView = NSVisualEffectView()
effectView.blendingMode = .behindWindow
effectView.material = .hudWindow
effectView.state = .active
effectView.isEmphasized = true
return effectView
}
func updateNSView(_ nsView: NSVisualEffectView, context: Context) {
}
}
class AppDelegate: NSObject, NSApplicationDelegate {
private var eventMonitor: Any?
func applicationDidFinishLaunching(_ notification: Notification) {
NSApp.mainMenu = nil
for window in NSApplication.shared.windows {
window.titlebarAppearsTransparent = true
window.backgroundColor = NSColor(white: 1, alpha: 0)
if let titlebarView = window.standardWindowButton(.closeButton)?.superview {
let blurView = NSVisualEffectView(frame: titlebarView.bounds)
blurView.blendingMode = .behindWindow
blurView.material = .hudWindow
blurView.state = .active
blurView.autoresizingMask = [.width, .height]
titlebarView.addSubview(blurView, positioned: .below, relativeTo: nil)
}
}
if let window = NSApp.windows.first {
window.minSize = NSSize(width: 792, height: 600)
}

View File

@@ -418,6 +418,20 @@ enum DownloadStatus: Equatable, Codable {
}
return false
}
var isCompleted: Bool {
if case .completed = self {
return true
}
return false
}
var isFailed: Bool {
if case .failed = self {
return true
}
return false
}
}
extension DownloadStatus.PrepareInfo: Equatable {}

View File

@@ -1,7 +0,0 @@
//
// Adobe Downloader
//
// Created by X1a0He on 2024/10/30.
//
import Foundation
import AppKit

View File

@@ -1,6 +0,0 @@
//
// NewExtensions.swift
// Adobe Downloader
//
// Created by X1a0He on 2/26/25.
//

View File

@@ -1,141 +0,0 @@
////
//// Adobe Downloader
////
//// Created by X1a0He on 2024/10/30.
////
//import Foundation
//
//
//class ProductsToDownload: ObservableObject, Codable {
// var sapCode: String
// var version: String
// var buildGuid: String
// var applicationJson: String?
// @Published var packages: [Package] = []
// @Published var completedPackages: Int = 0
//
// var totalPackages: Int {
// packages.count
// }
//
// init(sapCode: String, version: String, buildGuid: String, applicationJson: String = "") {
// self.sapCode = sapCode
// self.version = version
// self.buildGuid = buildGuid
// self.applicationJson = applicationJson
// }
//
// func updateCompletedPackages() {
// Task { @MainActor in
// completedPackages = packages.filter { $0.downloaded }.count
// objectWillChange.send()
// }
// }
//
// enum CodingKeys: String, CodingKey {
// case sapCode, version, buildGuid, applicationJson, packages
// }
//
// func encode(to encoder: Encoder) throws {
// var container = encoder.container(keyedBy: CodingKeys.self)
// try container.encode(sapCode, forKey: .sapCode)
// try container.encode(version, forKey: .version)
// try container.encode(buildGuid, forKey: .buildGuid)
// try container.encodeIfPresent(applicationJson, forKey: .applicationJson)
// try container.encode(packages, forKey: .packages)
// }
//
// required init(from decoder: Decoder) throws {
// let container = try decoder.container(keyedBy: CodingKeys.self)
// sapCode = try container.decode(String.self, forKey: .sapCode)
// version = try container.decode(String.self, forKey: .version)
// buildGuid = try container.decode(String.self, forKey: .buildGuid)
// applicationJson = try container.decodeIfPresent(String.self, forKey: .applicationJson)
// packages = try container.decode([Package].self, forKey: .packages)
// completedPackages = 0
// }
//}
//
//struct SapCodes: Identifiable {
// var id: String { sapCode }
// var sapCode: String
// var displayName: String
//}
//
//struct Sap: Codable, Equatable {
// var id: String { sapCode }
// var hidden: Bool
// var displayName: String
// var sapCode: String
// var versions: [String: Versions]
// var icons: [ProductIcon]
// var productsToDownload: [ProductsToDownload]? = nil
//
// enum CodingKeys: String, CodingKey {
// case hidden, displayName, sapCode, versions, icons
// }
//
// static func == (lhs: Sap, rhs: Sap) -> Bool {
// return lhs.sapCode == rhs.sapCode &&
// lhs.hidden == rhs.hidden &&
// lhs.displayName == rhs.displayName &&
// lhs.versions == rhs.versions &&
// lhs.icons == rhs.icons
// }
//
// struct Versions: Codable, Equatable {
// var sapCode: String
// var baseVersion: String
// var productVersion: String
// var apPlatform: String
// var dependencies: [Dependencies]
// var buildGuid: String
//
// struct Dependencies: Codable, Equatable {
// var sapCode: String
// var version: String
// }
// }
//
// struct ProductIcon: Codable, Equatable {
// let size: String
// let url: String
//
// var dimension: Int {
// let components = size.split(separator: "x")
// if components.count == 2,
// let dimension = Int(components[0]) {
// return dimension
// }
// return 0
// }
// }
//
// var isValid: Bool { !hidden }
//
// func getBestIcon() -> ProductIcon? {
// if let icon = icons.first(where: { $0.size == "192x192" }) {
// return icon
// }
// return icons.max(by: { $0.dimension < $1.dimension })
// }
//
// func hasValidVersions(allowedPlatform: [String]) -> Bool {
// if hidden { return false }
//
// for version in Array(versions.values).reversed() {
// if !version.buildGuid.isEmpty &&
// (!version.buildGuid.contains("/") || sapCode == "APRO") &&
// allowedPlatform.contains(version.apPlatform) {
// return true
// }
// }
// return false
// }
//}
//
//
//struct ProductsResponse: Codable {
// let products: [String: Sap]
// let cdn: String
//}

View File

@@ -57,10 +57,12 @@ struct ContentView: View {
filteredProducts: filteredProducts,
onRetry: { networkManager.retryFetchData() }
)
.background(Color(.clear))
.animation(.easeInOut, value: networkManager.loadingState)
.animation(.easeInOut, value: filteredProducts)
}
.sheet(isPresented: $showDownloadManager) {
.background(Color(.clear))
.sheet(isPresented: $showDownloadManager) {
DownloadManagerView()
}
.onChange(of: currentApiVersion) { newValue in

View File

@@ -1,118 +0,0 @@
//
// Adobe Downloader
//
// Created by X1a0He on 2024/10/30.
//
import Foundation
extension DownloadStatus {
var isCompleted: Bool {
if case .completed = self {
return true
}
return false
}
var isFailed: Bool {
if case .failed = self {
return true
}
return false
}
}
//class NewDownloadTask: Identifiable, ObservableObject, Equatable {
// let id = UUID()
// var sapCode: String
// let version: String
// let language: String
// let displayName: String
// let directory: URL
// var productsToDownload: [ProductsToDownload]
// var retryCount: Int
// let createAt: Date
// var displayInstallButton: Bool
// @Published var totalStatus: DownloadStatus?
// @Published var totalProgress: Double
// @Published var totalDownloadedSize: Int64
// @Published var totalSize: Int64
// @Published var totalSpeed: Double
// @Published var currentPackage: Package? {
// didSet {
// objectWillChange.send()
// }
// }
// let platform: String
//
// var status: DownloadStatus {
// totalStatus ?? .waiting
// }
//
// var destinationURL: URL { directory }
//
// var downloadedSize: Int64 {
// get { totalDownloadedSize }
// set { totalDownloadedSize = newValue }
// }
//
// var progress: Double {
// get { totalProgress }
// set { totalProgress = newValue }
// }
//
// var speed: Double {
// get { totalSpeed }
// set { totalSpeed = newValue }
// }
//
// var formattedTotalSize: String {
// ByteCountFormatter.string(fromByteCount: totalSize, countStyle: .file)
// }
//
// var formattedDownloadedSize: String {
// ByteCountFormatter.string(fromByteCount: totalDownloadedSize, countStyle: .file)
// }
//
// @Published var completedPackages: Int = 0
// @Published var totalPackages: Int = 0
//
// func setStatus(_ newStatus: DownloadStatus) {
// DispatchQueue.main.async {
// self.totalStatus = newStatus
// self.objectWillChange.send()
// }
// }
//
// func updateProgress(downloaded: Int64, total: Int64, speed: Double) {
// DispatchQueue.main.async {
// self.totalDownloadedSize = downloaded
// self.totalSize = total
// self.totalSpeed = speed
// self.totalProgress = total > 0 ? Double(downloaded) / Double(total) : 0
// self.objectWillChange.send()
// }
// }
//
// init(sapCode: String, version: String, language: String, displayName: String, directory: URL, productsToDownload: [ProductsToDownload] = [], retryCount: Int = 0, createAt: Date, totalStatus: DownloadStatus? = nil, totalProgress: Double, totalDownloadedSize: Int64 = 0, totalSize: Int64 = 0, totalSpeed: Double = 0, currentPackage: Package? = nil, platform: String) {
// self.sapCode = sapCode
// self.version = version
// self.language = language
// self.displayName = displayName
// self.directory = directory
// self.productsToDownload = productsToDownload
// self.retryCount = retryCount
// self.createAt = createAt
// self.totalStatus = totalStatus
// self.totalProgress = totalProgress
// self.totalDownloadedSize = totalDownloadedSize
// self.totalSize = totalSize
// self.totalSpeed = totalSpeed
// self.currentPackage = currentPackage
// self.displayInstallButton = sapCode != "APRO"
// self.platform = platform
// }
//
// static func == (lhs: NewDownloadTask, rhs: NewDownloadTask) -> Bool {
// return lhs.id == rhs.id
// }
//}

File diff suppressed because it is too large Load Diff

View File

@@ -18,6 +18,7 @@ actor InstallManager {
case installationFailed(String)
case cancelled
case permissionDenied
case installationFailedWithDetails(String, String)
var errorDescription: String? {
switch self {
@@ -25,6 +26,7 @@ actor InstallManager {
case .installationFailed(let message): return message
case .cancelled: return String(localized: "安装已取消")
case .permissionDenied: return String(localized: "权限被拒绝")
case .installationFailedWithDetails(let message, _): return message
}
}
}
@@ -71,6 +73,45 @@ actor InstallManager {
}
}
private func getAdobeInstallLogDetails() async -> String? {
let logPath = "/Library/Logs/Adobe/Installers/Install.log"
guard FileManager.default.fileExists(atPath: logPath) else {
return nil
}
do {
let logContent = try String(contentsOfFile: logPath, encoding: .utf8)
let lines = logContent.components(separatedBy: .newlines)
let fatalLines = lines.filter {
line in line.contains("FATAL:")
}
var uniqueLines: [String] = []
var seen = Set<String>()
for line in fatalLines {
if !seen.contains(line) {
seen.insert(line)
uniqueLines.append(line)
}
}
if uniqueLines.isEmpty, lines.count > 10 {
uniqueLines = Array(lines.suffix(10))
}
if !uniqueLines.isEmpty {
return uniqueLines.joined(separator: "\n")
}
return nil
} catch {
print("读取安装日志失败: \(error.localizedDescription)")
return nil
}
}
private func executeInstallation(
at appPath: URL,
progressHandler: @escaping (Double, String) -> Void
@@ -90,6 +131,27 @@ actor InstallManager {
throw InstallError.installationFailed("driver.xml 文件没有读取权限")
}
}
await MainActor.run {
progressHandler(0.0, String(localized: "正在清理安装环境..."))
}
let logFiles = [
"/Library/Logs/Adobe/Installers/Install.log",
]
for logFile in logFiles {
let removeCommand = "rm -f '\(logFile)'"
let result = await withCheckedContinuation { continuation in
PrivilegedHelperManager.shared.executeCommand(removeCommand) { result in
continuation.resume(returning: result)
}
}
if result.contains("Error") {
print("清理安装日志失败: \(logFile) - \(result)")
}
}
let installCommand = "sudo \"\(setupPath)\" --install=1 --driverXML=\"\(driverPath)\""
@@ -113,20 +175,12 @@ actor InstallManager {
return
} else {
let errorMessage: String
switch exitCode {
case 107:
errorMessage = String(localized: "架构或版本不一致 (退出代码: \(exitCode))")
case 103:
errorMessage = String(localized: "权限问题 (退出代码: \(exitCode))")
case 182:
errorMessage = String(localized: "安装文件不完整或损坏 (退出代码: \(exitCode))")
case -1:
errorMessage = String(localized: "Setup 组件未被处理 (退出代码: \(exitCode))")
default:
errorMessage = String(localized: "(退出代码: \(exitCode))")
errorMessage = String(localized: "错误代码(\(exitCode)),请查看日志详情并向开发者汇报")
if let logDetails = await self.getAdobeInstallLogDetails() {
continuation.resume(throwing: InstallError.installationFailedWithDetails(errorMessage, logDetails))
} else {
continuation.resume(throwing: InstallError.installationFailed(errorMessage))
}
progressHandler(0.0, errorMessage)
continuation.resume(throwing: InstallError.installationFailed(errorMessage))
return
}
}

View File

@@ -1,172 +0,0 @@
////
//// JSONParser.swift
//// Adobe Downloader
////
//// Created by X1a0He on 11/18/24.
////
//
//import Foundation
//
// struct ParseResult {
// var products: [String: Sap]
// var cdn: String
// }
//
//class JSONParser {
// static func parse(jsonString: String) throws -> ParseResult {
// guard let jsonData = jsonString.data(using: .utf8),
// let jsonObject = try JSONSerialization.jsonObject(with: jsonData) as? [String: Any] else {
// throw ParserError.invalidJSON
// }
// let apiVersion = Int(StorageData.shared.apiVersion) ?? 6
//
// return try parseProductsJSON(jsonObject: jsonObject, apiVersion: apiVersion)
// }
//
// private static func parseProductsJSON(jsonObject: [String: Any], apiVersion: Int) throws -> ParseResult {
// let cdnPath: [String]
// if apiVersion == 6 {
// cdnPath = ["channels", "channel"]
// } else {
// cdnPath = ["channel"]
// }
//
// func getValue(from dict: [String: Any], path: [String]) -> Any? {
// var current: Any = dict
// for key in path {
// guard let dict = current as? [String: Any],
// let value = dict[key] else {
// return nil
// }
// current = value
// }
// return current
// }
//
// var channelArray: [[String: Any]] = []
// if let channels = getValue(from: jsonObject, path: cdnPath) {
// if let array = channels as? [[String: Any]] {
// channelArray = array
// } else if let dict = channels as? [String: Any],
// let array = dict["channel"] as? [[String: Any]] {
// channelArray = array
// }
// }
//
// guard let firstChannel = channelArray.first,
// let cdn = (firstChannel["cdn"] as? [String: Any])?["secure"] as? String else {
// throw ParserError.missingCDN
// }
//
// var products = [String: Sap](minimumCapacity: 200)
//
// for channel in channelArray {
// let channelName = channel["name"] as? String
// let hidden = channelName != "ccm"
//
// guard let productsContainer = channel["products"] as? [String: Any],
// let productArray = productsContainer["product"] as? [[String: Any]] else {
// continue
// }
//
// for product in productArray {
// guard let sap = product["id"] as? String,
// let displayName = product["displayName"] as? String,
// let productVersion = product["version"] as? String else {
// continue
// }
//
// if products[sap] == nil {
// let icons = (product["productIcons"] as? [String: Any])?["icon"] as? [[String: Any]] ?? []
// let productIcons = icons.compactMap { icon -> Sap.ProductIcon? in
// guard let size = icon["size"] as? String,
// let value = icon["value"] as? String else {
// return nil
// }
// return Sap.ProductIcon(size: size, url: value)
// }
//
// products[sap] = Sap(
// hidden: hidden,
// displayName: displayName,
// sapCode: sap,
// versions: [:],
// icons: productIcons
// )
// }
//
// if let platforms = product["platforms"] as? [String: Any],
// let platformArray = platforms["platform"] as? [[String: Any]] {
//
// for platform in platformArray {
// guard let platformId = platform["id"] as? String,
// let languageSets = platform["languageSet"] as? [[String: Any]],
// let languageSet = languageSets.first else {
// continue
// }
//
// if let existingVersion = products[sap]?.versions[productVersion],
// StorageData.shared.allowedPlatform.contains(existingVersion.apPlatform) {
// break
// }
//
// var baseVersion = languageSet["baseVersion"] as? String ?? ""
// var buildGuid = languageSet["buildGuid"] as? String ?? ""
// var finalProductVersion = productVersion
//
// if sap == "APRO" {
// baseVersion = productVersion
// if apiVersion == 4 || apiVersion == 5 {
// if let appVersion = (languageSet["nglLicensingInfo"] as? [String: Any])?["appVersion"] as? String {
// finalProductVersion = appVersion
// }
// } else if apiVersion == 6 {
// if let builds = jsonObject["builds"] as? [String: Any],
// let buildArray = builds["build"] as? [[String: Any]] {
// for build in buildArray {
// if build["id"] as? String == sap && build["version"] as? String == baseVersion,
// let appVersion = (build["nglLicensingInfo"] as? [String: Any])?["appVersion"] as? String {
// finalProductVersion = appVersion
// break
// }
// }
// }
// }
//
// if let urls = languageSet["urls"] as? [String: Any],
// let manifestURL = urls["manifestURL"] as? String {
// buildGuid = manifestURL
// }
// }
//
// var dependencies: [Sap.Versions.Dependencies] = []
// if let deps = languageSet["dependencies"] as? [String: Any],
// let depArray = deps["dependency"] as? [[String: Any]] {
// dependencies = depArray.compactMap { dep in
// guard let sapCode = dep["sapCode"] as? String,
// let version = dep["baseVersion"] as? String else {
// return nil
// }
// return Sap.Versions.Dependencies(sapCode: sapCode, version: version)
// }
// }
//
// if !buildGuid.isEmpty {
// let version = Sap.Versions(
// sapCode: sap,
// baseVersion: baseVersion,
// productVersion: finalProductVersion,
// apPlatform: platformId,
// dependencies: dependencies,
// buildGuid: buildGuid
// )
// products[sap]?.versions[finalProductVersion] = version
// }
// }
// }
// }
// }
//
// return ParseResult(products: products, cdn: cdn)
// }
//}

View File

@@ -38,7 +38,7 @@ class NetworkManager: ObservableObject {
case idle
case installing(progress: Double, status: String)
case completed
case failed(Error)
case failed(Error, String? = nil)
}
init() {
@@ -62,7 +62,6 @@ class NetworkManager: ObservableObject {
}
}
func startDownload(productId: String, selectedVersion: String, language: String, destinationURL: URL) async throws {
print(destinationURL)
// globalCcmResult productId ProductInfo
guard let productInfo = globalCcmResult.products.first(where: { $0.id == productId }) else {
throw NetworkError.productNotFound
@@ -215,26 +214,20 @@ class NetworkManager: ObservableObject {
await MainActor.run {
self.installCommand = command
var errorDetails: String? = nil
var mainError = error
if let installError = error as? InstallManager.InstallError {
switch installError {
case .installationFailed(let message):
if message.contains("需要重新输入密码") {
Task {
await installProduct(at: path)
}
} else {
installationState = .failed(InstallManager.InstallError.installationFailed(message))
}
case .cancelled:
installationState = .failed(InstallManager.InstallError.cancelled)
case .setupNotFound:
installationState = .failed(InstallManager.InstallError.setupNotFound)
case .permissionDenied:
installationState = .failed(InstallManager.InstallError.permissionDenied)
case .installationFailedWithDetails(let message, let details):
errorDetails = details
mainError = InstallManager.InstallError.installationFailed(message)
default:
break
}
} else {
installationState = .failed(InstallManager.InstallError.installationFailed(error.localizedDescription))
}
installationState = .failed(mainError, errorDetails)
}
}
}
@@ -268,17 +261,18 @@ class NetworkManager: ObservableObject {
installationState = .completed
}
} catch {
if case InstallManager.InstallError.installationFailed(let message) = error,
message.contains("需要重新输入密码") {
await installProduct(at: path)
} else {
await MainActor.run {
if let installError = error as? InstallManager.InstallError {
installationState = .failed(installError)
} else {
installationState = .failed(error)
await MainActor.run {
var errorDetails: String? = nil
var mainError = error
if let installError = error as? InstallManager.InstallError {
if case .installationFailedWithDetails(let message, let details) = installError {
errorDetails = details
mainError = InstallManager.InstallError.installationFailed(message)
}
}
installationState = .failed(mainError, errorDetails)
}
}
}

View File

@@ -129,10 +129,6 @@ class NewDownloadUtils {
}
}
for dependencyToDownload in dependenciesToDownload {
print("\(dependencyToDownload.sapCode), \(dependencyToDownload.version), \(dependencyToDownload.buildGuid)")
}
for dependencyToDownload in dependenciesToDownload {
await MainActor.run {
task.setStatus(.preparing(DownloadStatus.PrepareInfo(
@@ -147,15 +143,72 @@ class NewDownloadUtils {
if !FileManager.default.fileExists(atPath: productDir.path) {
try FileManager.default.createDirectory(at: productDir, withIntermediateDirectories: true)
}
let jsonURL = productDir.appendingPathComponent("application.json")
try jsonString.write(to: jsonURL, atomically: true, encoding: String.Encoding.utf8)
guard let jsonData = jsonString.data(using: .utf8),
var processedJsonString = jsonString
if dependencyToDownload.sapCode == productInfo.id {
if let jsonData = jsonString.data(using: .utf8),
var appInfo = try JSONSerialization.jsonObject(with: jsonData) as? [String: Any] {
if var modules = appInfo["Modules"] as? [String: Any] {
modules["Module"] = [] as [[String: Any]]
appInfo["Modules"] = modules
}
if var packages = appInfo["Packages"] as? [String: Any],
let packageArray = packages["Package"] as? [[String: Any]] {
var filteredPackages: [[String: Any]] = []
for package in packageArray {
var shouldKeep = false
let packageType = package["Type"] as? String ?? "non-core"
let isCore = packageType == "core"
let condition = package["Condition"] as? String ?? ""
if isCore {
if condition.isEmpty {
shouldKeep = true
} else {
if condition.contains("[OSArchitecture]==\(AppStatics.architectureSymbol)") {
shouldKeep = true
}
if condition.contains("[installLanguage]==\(task.language)") || task.language == "ALL" {
shouldKeep = true
}
}
} else {
shouldKeep = (condition.contains("[installLanguage]==\(task.language)") || task.language == "ALL")
}
if shouldKeep {
filteredPackages.append(package)
}
}
packages["Package"] = filteredPackages
appInfo["Packages"] = packages
}
if let processedData = try? JSONSerialization.data(withJSONObject: appInfo, options: .prettyPrinted),
let processedString = String(data: processedData, encoding: .utf8) {
processedJsonString = processedString
}
}
}
let jsonURL = productDir.appendingPathComponent("application.json")
try processedJsonString.write(to: jsonURL, atomically: true, encoding: String.Encoding.utf8)
guard let jsonData = processedJsonString.data(using: .utf8),
let appInfo = try JSONSerialization.jsonObject(with: jsonData) as? [String: Any],
let packages = appInfo["Packages"] as? [String: Any],
let packageArray = packages["Package"] as? [[String: Any]] else {
throw NetworkError.invalidData("无法解析产品信息")
}
// TODO: module
// let modules = appInfo["Modules"] as? [String: Any]
// let moduleArray = modules?["Module"] as? [[String: Any]] ?? []
var corePackageCount = 0
var nonCorePackageCount = 0
@@ -175,134 +228,93 @@ class NewDownloadUtils {
2. Condition
3. Condition
i.
PS:
ACCApp
*/
for package in packageArray {
var shouldDownload = false
let packageType = package["Type"] as? String ?? "non-core"
let isCore = packageType == "core"
guard let downloadURL = package["Path"] as? String, !downloadURL.isEmpty else { continue }
let packageVersion: String = package["PackageVersion"] as? String ?? ""
let fullPackageName: String
let packageVersion: String
if let name = package["fullPackageName"] as? String, !name.isEmpty {
fullPackageName = name
packageVersion = package["PackageVersion"] as? String ?? ""
} else if let name = package["PackageName"] as? String, !name.isEmpty {
fullPackageName = "\(name).zip"
packageVersion = package["PackageVersion"] as? String ?? ""
} else {
continue
}
let downloadSize: Int64
if let sizeNumber = package["DownloadSize"] as? NSNumber {
downloadSize = sizeNumber.int64Value
} else if let sizeString = package["DownloadSize"] as? String,
let parsedSize = Int64(sizeString) {
downloadSize = parsedSize
} else if let sizeInt = package["DownloadSize"] as? Int {
downloadSize = Int64(sizeInt)
} else { continue }
if dependencyToDownload.sapCode != productInfo.id {
switch package["DownloadSize"] {
case let sizeNumber as NSNumber:
downloadSize = sizeNumber.int64Value
case let sizeString as String:
guard let parsedSize = Int64(sizeString) else { continue }
downloadSize = parsedSize
case let sizeInt as Int:
downloadSize = Int64(sizeInt)
default:
continue
}
let packageType = package["Type"] as? String ?? "non-core"
let isCore = packageType == "core"
let installLanguage = "[installLanguage]==\(task.language)"
let condition = package["Condition"] as? String ?? ""
var shouldDownload = false
if dependencyToDownload.sapCode == productInfo.id {
if isCore {
let installLanguage = "[installLanguage]==\(task.language)"
if let condition = package["Condition"] as? String {
if condition.isEmpty {
shouldDownload = true
} else {
if condition.contains("[OSArchitecture]==\(AppStatics.architectureSymbol)") {
shouldDownload = true
}
// if condition.contains("[OSArchitecture]==x64") {
// shouldDownload = true
// }
if condition.contains(installLanguage) || task.language == "ALL" {
shouldDownload = true
}
}
} else {
shouldDownload = true
}
shouldDownload = condition.isEmpty ||
condition.contains("[OSArchitecture]==\(AppStatics.architectureSymbol)") ||
condition.contains(installLanguage) || task.language == "ALL"
} else {
shouldDownload = false
shouldDownload = condition.contains(installLanguage) || task.language == "ALL"
}
} else {
let installLanguage = "[installLanguage]==\(task.language)"
if let condition = package["Condition"] as? String {
if condition.isEmpty {
shouldDownload = true
} else {
if condition.contains("[OSVersion]") {
let osVersion = ProcessInfo.processInfo.operatingSystemVersion
let currentVersion = Double("\(osVersion.majorVersion).\(osVersion.minorVersion)") ?? 0.0
let versionPattern = #"\[OSVersion\](>=|<=|<|>|==)([\d.]+)"#
let regex = try? NSRegularExpression(pattern: versionPattern)
let range = NSRange(condition.startIndex..<condition.endIndex, in: condition)
if let matches = regex?.matches(in: condition, range: range) {
var meetsAllConditions = true
for match in matches {
guard let operatorRange = Range(match.range(at: 1), in: condition),
let versionRange = Range(match.range(at: 2), in: condition),
let requiredVersion = Double(condition[versionRange]) else {
continue
}
let operatorSymbol = String(condition[operatorRange])
let meets = compareVersions(current: currentVersion, required: requiredVersion, operator: operatorSymbol)
if !meets {
meetsAllConditions = false
break
}
}
if meetsAllConditions {
shouldDownload = true
}
}
}
if condition.contains(installLanguage) || task.language == "ALL" {
shouldDownload = true
}
}
} else {
shouldDownload = true
}
}
if isCore {
corePackageCount += 1
} else {
nonCorePackageCount += 1
shouldDownload = condition.isEmpty ||
(condition.contains("[OSVersion]") && checkOSVersionCondition(condition)) ||
condition.contains(installLanguage) || task.language == "ALL"
}
isCore ? (corePackageCount += 1) : (nonCorePackageCount += 1)
if shouldDownload {
let newPackage = Package(
dependencyToDownload.packages.append(Package(
type: packageType,
fullPackageName: fullPackageName,
downloadSize: downloadSize,
downloadURL: downloadURL,
packageVersion: packageVersion
)
dependencyToDownload.packages.append(newPackage)
))
}
}
func checkOSVersionCondition(_ condition: String) -> Bool {
let osVersion = ProcessInfo.processInfo.operatingSystemVersion
let currentVersion = Double("\(osVersion.majorVersion).\(osVersion.minorVersion)") ?? 0.0
let versionPattern = #"\[OSVersion\](>=|<=|<|>|==)([\d.]+)"#
guard let regex = try? NSRegularExpression(pattern: versionPattern) else { return false }
let nsRange = NSRange(condition.startIndex..<condition.endIndex, in: condition)
let matches = regex.matches(in: condition, range: nsRange)
for match in matches {
guard match.numberOfRanges >= 3,
let operatorRange = Range(match.range(at: 1), in: condition),
let versionRange = Range(match.range(at: 2), in: condition),
let requiredVersion = Double(condition[versionRange]) else { continue }
let operatorSymbol = String(condition[operatorRange])
if !compareVersions(current: currentVersion, required: requiredVersion, operator: operatorSymbol) {
return false
}
}
return !matches.isEmpty
}
}
let finalProducts = dependenciesToDownload
@@ -490,8 +502,7 @@ class NewDownloadUtils {
)))
if let currentPackage = task.currentPackage {
let destinationDir = task.directory
.appendingPathComponent("\(task.productId ?? "")")
let destinationDir = task.directory.appendingPathComponent("\(task.productId)")
let fileURL = destinationDir.appendingPathComponent(currentPackage.fullPackageName)
try? FileManager.default.removeItem(at: fileURL)
}
@@ -714,27 +725,35 @@ class NewDownloadUtils {
}
//
let dependencies = languageSet.first?.dependencies.map { dependency in
let dependencies = (languageSet.first?.dependencies.map { dependency in
"""
<Dependency>
<SAPCode>\(productInfo.id)</SAPCode>
<BaseVersion>\(languageSet.first?.baseVersion)</BaseVersion>
<EsdDirectory>\(productInfo.id)</EsdDirectory>
</Dependency>
<Dependency>
<BuildGuid>\(dependency.buildGuid)</BuildGuid>
<BuildVersion>\(dependency.productVersion)</BuildVersion>
<CodexVersion>\(dependency.baseVersion)</CodexVersion>
<Platform>\(dependency.selectedPlatform)</Platform>
<SAPCode>\(dependency.sapCode)</SAPCode>
<EsdDirectory>\(dependency.sapCode)</EsdDirectory>
</Dependency>
"""
}.joined(separator: "\n")
}.joined(separator: "\n")) ?? ""
let buildGuid = productInfo.platforms.first?.languageSet.first?.buildGuid ?? ""
let buildVersion = languageSet.first?.productVersion ?? ""
return """
<DriverInfo>
<ProductInfo>
<n>Adobe \(displayName)</n>
<SAPCode>\(productInfo.id)</SAPCode>
<CodexVersion>\(version)</CodexVersion>
<Platform>mac</Platform>
<EsdDirectory>\(productInfo.id)</EsdDirectory>
<Dependencies>
\(dependencies)
</Dependencies>
<Modules></Modules>
<BuildGuid>\(buildGuid)</BuildGuid>
<BuildVersion>\(buildVersion)</BuildVersion>
<CodexVersion>\(productInfo.version)</CodexVersion>
<Platform>\(platform)</Platform>
<EsdDirectory>\(productInfo.id)</EsdDirectory>
<SAPCode>\(productInfo.id)</SAPCode>
</ProductInfo>
<RequestInfo>
<InstallDir>/Applications</InstallDir>
@@ -746,9 +765,11 @@ class NewDownloadUtils {
func downloadAPRO(task: NewDownloadTask, productInfo: Product) async throws {
let firstPlatform = productInfo.platforms.first
let buildGuid = firstPlatform?.languageSet.first?.buildGuid ?? ""
let productManifestURL = firstPlatform?.languageSet.first?.manifestURL ?? ""
let manifestURL = globalCdn + buildGuid
let manifestURL = globalCdn + productManifestURL
print("manifestURL")
print(manifestURL)
guard let url = URL(string: manifestURL) else {
throw NetworkError.invalidURL(manifestURL)
}
@@ -757,7 +778,6 @@ class NewDownloadUtils {
request.httpMethod = "GET"
var headers = NetworkConstants.adobeRequestHeaders
headers["x-adobe-build-guid"] = buildGuid
headers.forEach { request.setValue($0.value, forHTTPHeaderField: $0.key) }
@@ -775,6 +795,8 @@ class NewDownloadUtils {
throw NetworkError.invalidURL(downloadPath)
}
print("downloadURL \(downloadURL)")
let aproPackage = Package(
type: "dmg",
fullPackageName: "Adobe Downloader \(task.productId)_\(firstPlatform?.languageSet.first?.productVersion ?? "unknown")_\(firstPlatform?.id ?? "unknown").dmg",
@@ -783,8 +805,10 @@ class NewDownloadUtils {
packageVersion: ""
)
print(aproPackage)
await MainActor.run {
let product = DependenciesToDownload(sapCode: task.productId, version: firstPlatform?.languageSet.first?.productVersion ?? "unknown", buildGuid: buildGuid)
let product = DependenciesToDownload(sapCode: task.productId, version: firstPlatform?.languageSet.first?.productVersion ?? "unknown", buildGuid: "")
product.packages = [aproPackage]
task.dependenciesToDownload = [product]
task.totalSize = assetSize

View File

@@ -231,6 +231,8 @@ class NewJSONParser {
continue
}
if productDisplayName == "Creative Cloud" { continue }
let icons = (product["productIcons"] as? [String: Any])?["icon"] as? [[String: Any]] ?? []
let productIcons = icons.compactMap { icon -> Product.ProductIcon? in
guard let size = icon["size"] as? String,

View File

@@ -72,7 +72,7 @@ struct AboutView: View {
}
.tag("about_app")
}
.background(Color(NSColor.windowBackgroundColor))
.background(Color(.clear))
.frame(width: 600)
.onAppear {
selectedTab = "general_settings"

View File

@@ -294,7 +294,8 @@ struct AppCardView: View {
@StateObject private var viewModel: AppCardViewModel
@StorageValue(\.useDefaultLanguage) private var useDefaultLanguage
@StorageValue(\.defaultLanguage) private var defaultLanguage
@State private var isHovered = false
init(uniqueProduct: UniqueProduct) {
_viewModel = StateObject(wrappedValue: AppCardViewModel(uniqueProduct: uniqueProduct))
}
@@ -308,6 +309,26 @@ struct AppCardView: View {
DownloadButtonView(viewModel: viewModel)
}
}
.background(
RoundedRectangle(cornerRadius: 12)
.fill(isHovered ? Color(.controlBackgroundColor) : Color(.windowBackgroundColor).opacity(0.5))
.animation(.easeInOut(duration: 0.2), value: isHovered)
)
.overlay(
RoundedRectangle(cornerRadius: 12)
.stroke(isHovered ? Color.blue.opacity(0.5) : Color.gray.opacity(0.1), lineWidth: isHovered ? 2 : 1)
.animation(.easeInOut(duration: 0.2), value: isHovered)
)
.shadow(color: isHovered ? Color.black.opacity(0.1) : Color.black.opacity(0.05),
radius: isHovered ? 4 : 2,
x: 0,
y: isHovered ? 2 : 1)
.animation(.easeInOut(duration: 0.2), value: isHovered)
.onHover { hovering in
self.isHovered = hovering
}
.animation(.spring(response: 0.3, dampingFraction: 0.7), value: isHovered)
.contentShape(Rectangle())
.modifier(CardModifier())
.modifier(SheetModifier(viewModel: viewModel))
.modifier(AlertModifier(viewModel: viewModel, confirmRedownload: true))
@@ -417,9 +438,11 @@ private struct ProductInfoView: View {
MetricView(icon: "square.stack.3d.up", value: "\(modulesCount)")
}
}
.background(Color(.clear))
.font(.caption)
.foregroundColor(.secondary)
}
.background(Color(.clear))
}
}
@@ -466,10 +489,7 @@ private struct CardModifier: ViewModifier {
func body(content: Content) -> some View {
content
.background(
RoundedRectangle(cornerRadius: AppCardConstants.cornerRadius)
.fill(Color(NSColor.windowBackgroundColor))
)
.background(Color(NSColor.clear))
.overlay(
RoundedRectangle(cornerRadius: AppCardConstants.cornerRadius)
.stroke(Color.gray.opacity(AppCardConstants.strokeOpacity),

View File

@@ -5,11 +5,11 @@ struct BannerView: View {
HStack(spacing: 8) {
Image(systemName: "exclamationmark.triangle.fill")
.foregroundColor(.orange)
Text("Adobe Downloader 完全免费: https://github.com/X1a0He/Adobe-Downloader")
Text("Adobe Downloader 完全开源免费: https://github.com/X1a0He/Adobe-Downloader")
}
.frame(maxWidth: .infinity, alignment: .center)
.padding(.horizontal)
.padding(.bottom, 5)
.background(Color(NSColor.windowBackgroundColor))
.background(Color(.clear))
}
}
}

View File

@@ -58,6 +58,7 @@ struct DownloadManagerView: View {
removeTask: removeTask
)
}
.background(Color(.clear))
.frame(width:800, height: 600)
}
}
@@ -86,16 +87,7 @@ private struct DownloadManagerToolbar: View {
Divider()
}
.background(
LinearGradient(
gradient: Gradient(colors: [
Color(NSColor.windowBackgroundColor).opacity(0.95),
Color(NSColor.windowBackgroundColor)
]),
startPoint: .top,
endPoint: .bottom
)
)
.background(Color(NSColor.clear))
.shadow(color: Color.black.opacity(0.1), radius: 2, x: 0, y: 1)
}
}
@@ -143,6 +135,9 @@ private struct ToolbarButtons: View {
if case .completed = task.status {
return true
}
if case .failed = task.status {
return true
}
return false
}
globalNetworkManager.updateDockBadge()
@@ -160,6 +155,7 @@ private struct ToolbarButtons: View {
}
.buttonStyle(BeautifulButtonStyle(baseColor: .red))
}
.background(Color(NSColor.clear))
}
}
@@ -184,7 +180,7 @@ private struct DownloadTaskList: View {
.padding(.horizontal)
.padding(.vertical, 8)
}
.background(Color(NSColor.windowBackgroundColor))
.background(Color(NSColor.clear))
}
}

View File

@@ -258,7 +258,7 @@ struct DownloadProgressView: View {
},
onRetry: nil
)
} else if case .failed(let error) = globalNetworkManager.installationState {
} else if case .failed(let error, let errorDetails) = globalNetworkManager.installationState {
InstallProgressView(
productName: task.displayName,
progress: 0,
@@ -270,7 +270,8 @@ struct DownloadProgressView: View {
Task {
await globalNetworkManager.retryInstallation(at: task.directory)
}
}
},
errorDetails: errorDetails
)
} else {
InstallProgressView(
@@ -285,7 +286,7 @@ struct DownloadProgressView: View {
)
}
}
.frame(minWidth: 400, minHeight: 200)
.frame(minWidth: 700, minHeight: 200)
.background(Color(NSColor.windowBackgroundColor))
}
}

View File

@@ -12,6 +12,21 @@ struct InstallProgressView: View {
let status: String
let onCancel: () -> Void
let onRetry: (() -> Void)?
let errorDetails: String?
init(productName: String,
progress: Double,
status: String,
onCancel: @escaping () -> Void,
onRetry: (() -> Void)? = nil,
errorDetails: String? = nil) {
self.productName = productName
self.progress = progress
self.status = status
self.onCancel = onCancel
self.onRetry = onRetry
self.errorDetails = errorDetails
}
private var isCompleted: Bool {
progress >= 1.0 || status == String(localized: "安装完成")
@@ -79,7 +94,8 @@ struct InstallProgressView: View {
if isFailed {
ErrorSection(
status: status,
isFailed: true
isFailed: true,
errorDetails: errorDetails
)
}
@@ -91,11 +107,10 @@ struct InstallProgressView: View {
)
}
.padding()
.frame(minWidth: 500)
.frame(minWidth: 600)
.background(
RoundedRectangle(cornerRadius: 10)
.fill(Color(NSColor.windowBackgroundColor))
.shadow(color: Color.black.opacity(0.1), radius: 5, x: 0, y: 2)
.fill(Color(NSColor.clear))
)
}
}
@@ -147,6 +162,15 @@ private struct ProgressSection: View {
private struct ErrorSection: View {
let status: String
let isFailed: Bool
let errorDetails: String?
init(status: String,
isFailed: Bool,
errorDetails: String? = nil) {
self.status = status
self.isFailed = isFailed
self.errorDetails = errorDetails
}
var body: some View {
VStack(alignment: .leading, spacing: 12) {
@@ -175,6 +199,37 @@ private struct ErrorSection: View {
.strokeBorder(Color.red.opacity(0.2), lineWidth: 1)
)
if let errorDetails = errorDetails, !errorDetails.isEmpty {
VStack(alignment: .leading, spacing: 6) {
HStack(spacing: 6) {
Image(systemName: "doc.text.magnifyingglass")
.foregroundColor(.orange.opacity(0.7))
.font(.system(size: 14))
Text("日志详情")
.font(.system(size: 13, weight: .medium))
.foregroundColor(.primary.opacity(0.8))
}
ScrollView {
Text(errorDetails)
.font(.system(size: 11, design: .monospaced))
.foregroundColor(.secondary)
.textSelection(.enabled)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(10)
.background(
RoundedRectangle(cornerRadius: 8)
.fill(Color.orange.opacity(0.05))
)
.overlay(
RoundedRectangle(cornerRadius: 8)
.strokeBorder(Color.orange.opacity(0.2), lineWidth: 1)
)
}
.frame(maxHeight: 120)
}
}
if isFailed {
HStack(spacing: 6) {
Image(systemName: "terminal.fill")
@@ -351,7 +406,8 @@ private struct CommandPopover: View {
progress: 0.45,
status: "正在安装核心组件...",
onCancel: {},
onRetry: nil
onRetry: nil,
errorDetails: nil
)
.environmentObject(networkManager)
}
@@ -363,7 +419,8 @@ private struct CommandPopover: View {
progress: 0.0,
status: "安装失败: 权限被拒绝",
onCancel: {},
onRetry: {}
onRetry: {},
errorDetails: "详细错误日志"
)
.environmentObject(networkManager)
.onAppear {
@@ -378,7 +435,8 @@ private struct CommandPopover: View {
progress: 1.0,
status: "安装完成",
onCancel: {},
onRetry: nil
onRetry: nil,
errorDetails: nil
)
.environmentObject(networkManager)
}
@@ -390,7 +448,8 @@ private struct CommandPopover: View {
progress: 0.75,
status: "正在安装...",
onCancel: {},
onRetry: nil
onRetry: nil,
errorDetails: nil
)
.environmentObject(networkManager)
.preferredColorScheme(.dark)

View File

@@ -7,9 +7,6 @@ struct MainContentView: View {
var body: some View {
ZStack {
Color(NSColor.windowBackgroundColor)
.ignoresSafeArea()
switch loadingState {
case .idle, .loading:
ProgressView("正在加载...")
@@ -50,6 +47,7 @@ struct MainContentView: View {
}
}
}
.background(Color(.clear))
}
}
@@ -97,5 +95,6 @@ struct ProductGridView: View {
}
.padding(.bottom, 16)
}
.background(Color(.clear))
}
}
}

View File

@@ -224,6 +224,6 @@ struct ToolbarView: View {
}
.padding(.horizontal)
.padding(.vertical, 8)
.background(Color(NSColor.windowBackgroundColor))
.background(Color(.clear))
}
}

View File

@@ -320,7 +320,7 @@ private struct ExistingPathButton: View {
var body: some View {
if isVisible {
Text("可能已存在目录")
Text("已存在")
.font(.system(size: 11, weight: .medium))
.foregroundColor(.blue.opacity(0.9))
.padding(.horizontal, 8)

View File

@@ -26,9 +26,6 @@
}
}
}
},
"(可能导致处理失败)" : {
},
"(将导致无法使用安装功能)" : {
"extractionState" : "stale",
@@ -40,12 +37,6 @@
}
}
}
},
"(无法使用安装功能)" : {
},
"(退出代码: %lld)" : {
},
"/" : {
@@ -156,6 +147,12 @@
}
}
}
},
"✅" : {
},
"❌" : {
},
"🔔 即将下载 %@ (%@) 版本 🔔" : {
"extractionState" : "stale",
@@ -173,12 +170,8 @@
},
"Adobe Downloader %@" : {
},
"Adobe Downloader 完全免费: https://github.com/X1a0He/Adobe-Downloader" : {
},
"Adobe Downloader 完全开源免费: https://github.com/X1a0He/Adobe-Downloader" : {
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@@ -314,7 +307,6 @@
},
"Debug 模式" : {
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@@ -452,9 +444,15 @@
},
"macOS %@" : {
},
"Match:" : {
},
"OK" : {
},
"Reason:" : {
},
"Setup 组件安装成功" : {
"extractionState" : "stale",
@@ -486,9 +484,6 @@
}
}
}
},
"Setup 组件未被处理 (退出代码: %lld)" : {
},
"Setup未备份提示" : {
"localizations" : {
@@ -499,6 +494,9 @@
}
}
}
},
"Target:" : {
},
"v%@" : {
@@ -1152,6 +1150,7 @@
}
},
"可能已存在目录" : {
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@@ -1418,9 +1417,6 @@
}
}
}
},
"安装文件不完整或损坏 (退出代码: %lld)" : {
},
"安装状态: " : {
@@ -1468,7 +1464,6 @@
}
},
"将执行的命令:" : {
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@@ -1512,7 +1507,6 @@
},
"展开全部" : {
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@@ -1539,6 +1533,7 @@
},
"已处理" : {
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@@ -1549,6 +1544,7 @@
}
},
"已备份" : {
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@@ -1567,6 +1563,9 @@
}
}
}
},
"已存在" : {
},
"已安装 (build %@)" : {
"localizations" : {
@@ -1762,7 +1761,6 @@
}
},
"折叠全部" : {
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@@ -2014,6 +2012,9 @@
}
}
}
},
"日志详情" : {
},
"是否确认重新下载?这将覆盖现有的安装程序。" : {
"localizations" : {
@@ -2106,12 +2107,6 @@
}
}
}
},
"未处理" : {
},
"未备份" : {
},
"未安装" : {
"localizations" : {
@@ -2209,15 +2204,12 @@
}
}
}
},
"权限问题 (退出代码: %lld)" : {
},
"架构或版本不一致 (退出代码: %lld)" : {
},
"查看" : {
},
"查看持久化文件" : {
},
"检查中" : {
"localizations" : {
@@ -2413,6 +2405,9 @@
}
}
}
},
"正在清理安装环境..." : {
},
"正在清理安装记录..." : {
"localizations" : {
@@ -3150,6 +3145,9 @@
}
}
}
},
"错误代码(%lld),请查看日志详情并向开发者汇报" : {
},
"错误详情" : {