mirror of
https://github.com/exituser/Pearcleaner.git
synced 2026-09-17 10:48:52 +00:00
3.6.2
This commit is contained in:
@@ -27,6 +27,7 @@ class DeeplinkManager {
|
||||
if url.pathExtension == "app" {
|
||||
handleAppBundle(url: url, appState: appState, locations: locations)
|
||||
} else if url.scheme == DeepLinkConstants.scheme,
|
||||
// This handles SentinelMonitor FileWatcher
|
||||
url.host == DeepLinkConstants.host,
|
||||
let components = URLComponents(url: url, resolvingAgainstBaseURL: true),
|
||||
let queryItems = components.queryItems {
|
||||
|
||||
@@ -200,14 +200,14 @@ func showAppInFiles(appInfo: AppInfo, appState: AppState, locations: Locations,
|
||||
|
||||
// Move files to trash using applescript/Finder so it asks for user password if needed
|
||||
func moveFilesToTrash(appState: AppState, at fileURLs: [URL], completion: @escaping (Bool) -> Void = {_ in }) {
|
||||
@AppStorage("settings.sentinel.enable") var sentinel: Bool = false
|
||||
if sentinel {
|
||||
launchctl(load: false)
|
||||
}
|
||||
// Stop Sentinel FileWatcher momentarily to ignore .app bundle being sent to Trash
|
||||
sendStopNotificationFW()
|
||||
|
||||
updateOnBackground {
|
||||
let posixFiles = fileURLs.map { "POSIX file \"\($0.path)\", " }.joined().dropLast(3)
|
||||
let posixFiles = fileURLs.map { item in
|
||||
return "POSIX file \"\(item.path)\"" + (item == fileURLs.last ? "" : ", ")}.joined()
|
||||
let scriptSource = """
|
||||
tell application \"Finder\" to delete { \(posixFiles)" }
|
||||
tell application \"Finder\" to delete { \(posixFiles) }
|
||||
"""
|
||||
|
||||
var error: NSDictionary?
|
||||
|
||||
@@ -66,10 +66,10 @@ func checkForUpdate(appState: AppState, manual: Bool = false) {
|
||||
|
||||
func downloadUpdate(appState: AppState) {
|
||||
updateOnMain {
|
||||
appState.progressBar.0 = "Getting update file links ready"
|
||||
appState.progressBar.0 = "UPDATER: Getting update link"
|
||||
appState.progressBar.1 = 0.1
|
||||
}
|
||||
|
||||
let fileManager = FileManager.default
|
||||
guard let latestRelease = appState.releases.first else { return }
|
||||
guard let asset = latestRelease.assets.first else { return }
|
||||
guard let url = URL(string: asset.url) else { return }
|
||||
@@ -78,38 +78,37 @@ func downloadUpdate(appState: AppState) {
|
||||
|
||||
let downloadTask = URLSession.shared.downloadTask(with: request) { localURL, urlResponse, error in
|
||||
updateOnMain {
|
||||
appState.progressBar.0 = "Downloading update file"
|
||||
appState.progressBar.0 = "UPDATER: Starting download of update file"
|
||||
appState.progressBar.1 = 0.2
|
||||
}
|
||||
|
||||
guard let localURL = localURL else { return }
|
||||
|
||||
let fileManager = FileManager.default
|
||||
let destinationURL = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!.appendingPathComponent("Pearcleaner").appendingPathComponent("\(asset.name)")
|
||||
|
||||
let destinationURL = FileManager.default.temporaryDirectory.appendingPathComponent("\(asset.name)")
|
||||
|
||||
do {
|
||||
if fileManager.fileExists(atPath: destinationURL.path) {
|
||||
try? fileManager.removeItem(at: destinationURL)
|
||||
}
|
||||
|
||||
updateOnMain {
|
||||
appState.progressBar.0 = "Moving update file to Application Support"
|
||||
appState.progressBar.0 = "UPDATER: File downloaded to temp directory"
|
||||
appState.progressBar.1 = 0.3
|
||||
}
|
||||
try fileManager.moveItem(at: localURL, to: destinationURL)
|
||||
|
||||
updateOnMain {
|
||||
appState.progressBar.0 = "UPDATER: File renamed using asset name"
|
||||
appState.progressBar.1 = 0.4
|
||||
}
|
||||
|
||||
try fileManager.moveItem(at: localURL, to: destinationURL)
|
||||
|
||||
UnzipAndReplace(DownloadedFileURL: destinationURL.path, appState: appState)
|
||||
|
||||
updateOnMain {
|
||||
appState.progressBar.0 = "Done, please restart!"
|
||||
appState.progressBar.1 = 1.0
|
||||
appState.updateAvailable = false
|
||||
}
|
||||
|
||||
|
||||
} catch {
|
||||
printOS("Error moving downloaded file: \(error.localizedDescription)")
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
downloadTask.resume()
|
||||
@@ -122,7 +121,7 @@ func UnzipAndReplace(DownloadedFileURL fileURL: String, appState: AppState) {
|
||||
|
||||
do {
|
||||
updateOnMain {
|
||||
appState.progressBar.0 = "Deleting existing application"
|
||||
appState.progressBar.0 = "UPDATER: Removing currently installed application bundle"
|
||||
appState.progressBar.1 = 0.5
|
||||
}
|
||||
|
||||
@@ -130,11 +129,10 @@ func UnzipAndReplace(DownloadedFileURL fileURL: String, appState: AppState) {
|
||||
try fileManager.removeItem(atPath: appBundle)
|
||||
|
||||
updateOnMain {
|
||||
appState.progressBar.0 = "Unziping new update file to original Pearcleaner location"
|
||||
appState.progressBar.0 = "UPDATER: Unziping file to original install location"
|
||||
appState.progressBar.1 = 0.6
|
||||
}
|
||||
|
||||
|
||||
// Unzip the downloaded update file to your app's bundle path
|
||||
let process = Process()
|
||||
process.executableURL = URL(fileURLWithPath: "/usr/bin/ditto")
|
||||
@@ -146,13 +144,18 @@ func UnzipAndReplace(DownloadedFileURL fileURL: String, appState: AppState) {
|
||||
process.waitUntilExit()
|
||||
|
||||
updateOnMain {
|
||||
appState.progressBar.0 = "Deleting update file"
|
||||
appState.progressBar.0 = "UPDATER: Removing file from temp directory"
|
||||
appState.progressBar.1 = 0.8
|
||||
}
|
||||
|
||||
// After unzipping, remove the update file
|
||||
try fileManager.removeItem(atPath: fileURL)
|
||||
|
||||
updateOnMain {
|
||||
appState.progressBar.0 = "UPDATER: Completed, please restart!"
|
||||
appState.progressBar.1 = 1.0
|
||||
appState.updateAvailable = false
|
||||
}
|
||||
|
||||
} catch {
|
||||
printOS("Error updating the app: \(error)")
|
||||
|
||||
@@ -121,11 +121,13 @@ func findAndHideWindows(named titles: [String]) {
|
||||
}
|
||||
|
||||
func findAndSetWindowFrame(named titles: [String], windowSettings: WindowSettings) {
|
||||
for title in titles {
|
||||
if let window = NSApp.windows.first(where: { $0.title == title }) {
|
||||
window.isRestorable = false
|
||||
let frame = windowSettings.loadWindowSettings()
|
||||
window.setFrame(frame, display: true)
|
||||
windowSettings.registerDefaultWindowSettings() {
|
||||
for title in titles {
|
||||
if let window = NSApp.windows.first(where: { $0.title == title }) {
|
||||
window.isRestorable = false
|
||||
let frame = windowSettings.loadWindowSettings()
|
||||
window.setFrame(frame, display: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -279,16 +281,17 @@ func killApp(appId: String, completion: @escaping () -> Void = {}) {
|
||||
}
|
||||
|
||||
// Remove app from cache
|
||||
func removeApp(appState: AppState, withId id: UUID) {
|
||||
func removeApp(appState: AppState, withPath path: URL) {
|
||||
@AppStorage("settings.general.brew") var brew: Bool = false
|
||||
DispatchQueue.main.async {
|
||||
|
||||
// Remove from sortedApps if found
|
||||
if let index = appState.sortedApps.firstIndex(where: { $0.id == id }) {
|
||||
if let index = appState.sortedApps.firstIndex(where: { $0.path == path }) {
|
||||
appState.sortedApps.remove(at: index)
|
||||
return // Exit the function if the app was found and removed
|
||||
// return // Exit the function if the app was found and removed
|
||||
}
|
||||
// Remove from appInfoStore if found
|
||||
if let index = appState.appInfoStore.firstIndex(where: { $0.id == id }) {
|
||||
if let index = appState.appInfoStore.firstIndex(where: { $0.path == path }) {
|
||||
appState.appInfoStore.remove(at: index)
|
||||
}
|
||||
// Brew cleanup if enabled
|
||||
@@ -410,13 +413,13 @@ extension String {
|
||||
|
||||
|
||||
// --- Trash Relationship ---
|
||||
extension FileManager {
|
||||
public func isInTrash(_ file: URL) -> Bool {
|
||||
var relationship: URLRelationship = .other
|
||||
try? getRelationship(&relationship, of: .trashDirectory, in: .userDomainMask, toItemAt: file)
|
||||
return relationship == .contains
|
||||
}
|
||||
}
|
||||
//extension FileManager {
|
||||
// public func isInTrash(_ file: URL) -> Bool {
|
||||
// var relationship: URLRelationship = .other
|
||||
// try? getRelationship(&relationship, of: .trashDirectory, in: .userDomainMask, toItemAt: file)
|
||||
// return relationship == .contains
|
||||
// }
|
||||
//}
|
||||
|
||||
// --- Extend print command to also output to the Console ---
|
||||
func printOS(_ items: Any..., separator: String = " ", terminator: String = "\n") {
|
||||
@@ -518,37 +521,6 @@ func isSupportedFileType(at path: String) -> Bool {
|
||||
}
|
||||
|
||||
|
||||
// Alerts
|
||||
func presentAlert(appState: AppState) -> Alert {
|
||||
|
||||
switch appState.alertType {
|
||||
case .update:
|
||||
return Alert(title: Text("Update Available 🥳"), message: Text("You may choose to install the update now, otherwise you may check again later from Settings"), primaryButton: .default(Text("Install")) {
|
||||
downloadUpdate(appState: appState)
|
||||
appState.alertType = .off
|
||||
}, secondaryButton: .cancel())
|
||||
case .no_update:
|
||||
return Alert(title: Text("No Updates 😌"), message: Text("Pearcleaner is on the latest release available"), primaryButton: .cancel(Text("Okay")), secondaryButton: .default(Text("Force Update")) {
|
||||
downloadUpdate(appState: appState)
|
||||
appState.alertType = .off
|
||||
})
|
||||
case .diskAccess:
|
||||
return Alert(title: Text("Permissions"), message: Text("Pearcleaner requires Full Disk and Accessibility permissions. Drag the app into the Full Disk and Accessibility pane to enable or toggle On if already present."), primaryButton: .default(Text("Allow in Settings")) {
|
||||
if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles") {
|
||||
NSWorkspace.shared.open(url)
|
||||
}
|
||||
appState.alertType = .off
|
||||
}, secondaryButton: .cancel(Text("Later")))
|
||||
case .restartApp:
|
||||
return Alert(title: Text("Update Completed!"), message: Text("The application has been updated to the latest version, would you like to restart now?"), primaryButton: .default(Text("Restart")) {
|
||||
appState.alertType = .off
|
||||
relaunchApp()
|
||||
}, secondaryButton: .cancel(Text("Later")))
|
||||
case .off:
|
||||
return Alert(title: Text(""))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// --- Pearcleaner Uninstall --
|
||||
@@ -564,16 +536,15 @@ func uninstallPearcleaner(appState: AppState, locations: Locations) {
|
||||
AppPathFinder(appInfo: appInfo!, appState: appState, locations: locations, completion: {
|
||||
// Kill Pearcleaner and tell Finder to trash the files
|
||||
let selectedItemsArray = Array(appState.selectedItems).filter { !$0.path.contains(".Trash") }
|
||||
let posixFiles = selectedItemsArray.map { "POSIX file \"\($0.path)\", " }.joined().dropLast(3)
|
||||
let posixFiles = selectedItemsArray.map { item in
|
||||
return "POSIX file \"\(item.path)\"" + (item == selectedItemsArray.last ? "" : ", ")}.joined()
|
||||
let scriptSource = """
|
||||
tell application \"Finder\" to delete { \(posixFiles)" }
|
||||
tell application \"Finder\" to delete { \(posixFiles) }
|
||||
"""
|
||||
let task = Process()
|
||||
task.launchPath = "/bin/sh"
|
||||
task.arguments = ["-c", "sleep 1; osascript -e '\(scriptSource)'"]
|
||||
task.launch()
|
||||
|
||||
NSApp.terminate(nil)
|
||||
exit(0)
|
||||
}).findPaths()
|
||||
}
|
||||
@@ -653,7 +624,13 @@ func launchctl(load: Bool, completion: @escaping () -> Void = {}) {
|
||||
}
|
||||
|
||||
|
||||
func sendStartNotificationFW() {
|
||||
DistributedNotificationCenter.default().postNotificationName(Notification.Name("Pearcleaner.StartFileWatcher"), object: nil, userInfo: nil, deliverImmediately: true)
|
||||
}
|
||||
|
||||
func sendStopNotificationFW() {
|
||||
DistributedNotificationCenter.default().postNotificationName(Notification.Name("Pearcleaner.StopFileWatcher"), object: nil, userInfo: nil, deliverImmediately: true)
|
||||
}
|
||||
|
||||
|
||||
func getCurrentTimestamp() -> String {
|
||||
|
||||
Reference in New Issue
Block a user