This commit is contained in:
Alin
2024-06-14 17:44:33 -06:00
parent ca0785f275
commit 1f2e1202a8
12 changed files with 345 additions and 137 deletions
+26 -11
View File
@@ -153,17 +153,15 @@ class AppPathFinder {
for condition in conditions {
if bundleIdentifierL.contains(condition.bundle_id) {
// Exclude and include keywords
let hasIncludeKeyword = condition.include.contains(where: itemL.contains)
// Exclude keywords
let hasExcludeKeyword = condition.exclude.contains(where: itemL.contains)
if hasExcludeKeyword {
return false
}
// Include keywords
let hasIncludeKeyword = condition.include.contains(where: itemL.contains)
if hasIncludeKeyword {
if !condition.exclude.contains(where: itemL.contains) {
return true
}
return true
}
}
}
@@ -230,7 +228,7 @@ class AppPathFinder {
}
private func handleOutliers() -> [URL] {
private func handleOutliers(include: Bool = true) -> [URL] {
var outliers: [URL] = []
let bundleIdentifier = self.appInfo.bundleIdentifier.pearFormat()
@@ -239,14 +237,24 @@ class AppPathFinder {
bundleIdentifier.contains(condition.bundle_id)
}
for condition in matchingConditions {
if let forceIncludes = condition.includeForce {
for path in forceIncludes {
if let url = URL(string: path), FileManager.default.fileExists(atPath: url.path) {
outliers.append(url)
if include {
// Handle includeForce
if let forceIncludes = condition.includeForce {
for path in forceIncludes {
outliers.append(path)
}
}
} else {
// Handle excludeForce
if let excludeForce = condition.excludeForce {
for path in excludeForce {
outliers.append(path)
}
}
}
}
return outliers
@@ -256,6 +264,7 @@ class AppPathFinder {
DispatchQueue.global(qos: .userInitiated).async {
let allContainers = self.getAllContainers(bundleURL: self.appInfo.path)
let outliers = self.handleOutliers()
let outliersEx = self.handleOutliers(include: false)
var tempCollection: [URL] = []
self.collectionAccessQueue.sync {
tempCollection = self.collection
@@ -263,6 +272,12 @@ class AppPathFinder {
tempCollection.append(contentsOf: allContainers)
tempCollection.append(contentsOf: outliers)
// Remove URLs based on outliersExcludes
let excludePaths = outliersEx.map { $0.path }
tempCollection.removeAll { url in
excludePaths.contains(url.path)
}
// Sort and standardize URLs to ensure consistent comparisons
let sortedCollection = tempCollection.map { $0.standardizedFileURL }.sorted(by: { $0.path < $1.path })
var filteredCollection: [URL] = []
+98 -51
View File
@@ -6,13 +6,32 @@
//
import Foundation
import SwiftData
struct Condition: Decodable {
struct Condition: Codable {
var bundle_id: String
var include: [String]
var exclude: [String]
var includeForce: [String]?
var includeForce: [URL]?
var excludeForce: [URL]?
init(bundle_id: String, include: [String], exclude: [String], includeForce: [String]? = nil, excludeForce: [String]? = nil) {
self.bundle_id = bundle_id.pearFormat()
self.include = include.map { $0.pearFormat() }
self.exclude = exclude.map { $0.pearFormat() }
self.includeForce = includeForce?.compactMap { path in
if let url = URL(string: path), FileManager.default.fileExists(atPath: url.path) {
return url
}
return nil
}
self.excludeForce = excludeForce?.compactMap { path in
if let url = URL(string: path), FileManager.default.fileExists(atPath: url.path) {
return url
}
return nil
}
}
}
struct SkipCondition {
@@ -25,70 +44,75 @@ struct SkipCondition {
// Conditions for some apps that need to include/exclude certain files/folders when names are more complicated
var conditions: [Condition] = [
Condition(
bundle_id: "comappledtxcode",
include: ["comappledt", "xcode", "simulator"],
exclude: ["comrobotsandpencilsxcodesapp", "comoneminutegamesxcodecleaner", "iohyperappxcodecleaner", "xcodesjson"],
bundle_id: "com.apple.dt.xcode",
include: ["com.apple.dt", "xcode", "simulator"],
exclude: ["com.robotsandpencils.xcodesapp", "com.oneminutegames.xcodecleaner", "io.hyperapp.xcodecleaner", "xcodes.json"],
includeForce: ["\(home)/Library/Containers/com.apple.iphonesimulator.ShareExtension"]
),
Condition(
bundle_id: "comrobotsandpencilsxcodesapp",
bundle_id: "com.robotsandpencils.xcodesapp",
include: [],
exclude: ["comappledtxcode", "comoneminutegamesxcodecleaner", "iohyperappxcodecleaner"]
exclude: ["com.apple.dt.xcode", "com.oneminutegames.xcodecleaner", "io.hyperapp.xcodecleaner"]
),
Condition(
bundle_id: "iohyperappxcodecleaner",
bundle_id: "io.hyperapp.xcodecleaner",
include: [],
exclude: ["comrobotsandpencilsxcodesapp", "comoneminutegamesxcodecleaner", "comappledtxcode", "xcodesjson"]
exclude: ["com.robotsandpencils.xcodesapp", "com.oneminutegames.xcodecleaner", "com.apple.dt.xcode", "xcodes.json"]
),
Condition(
bundle_id: "uszoomxos",
bundle_id: "us.zoom.xos",
include: ["zoom"],
exclude: []
),
Condition(
bundle_id: "combravebrowser",
bundle_id: "com.brave.browser",
include: ["brave"],
exclude: []
),
Condition(
bundle_id: "comoktamobile",
bundle_id: "com.okta.mobile",
include: ["okta"],
exclude: []
),
Condition(
bundle_id: "comgooglechrome",
bundle_id: "com.google.chrome",
include: ["google", "chrome"],
exclude: ["iterm", "chromefeaturestate"]
),
Condition(
bundle_id: "commicrosoftedgemac",
bundle_id: "com.microsoft.edgemac",
include: ["microsoft"],
exclude: ["vscode", "rdc", "appcenter", "office", "oneauth"]
),
Condition(
bundle_id: "orgmozillafirefox",
bundle_id: "org.mozilla.firefox",
include: ["mozilla", "firefox"],
exclude: []
),
Condition(
bundle_id: "comlogioptionsplus",
bundle_id: "org.mozilla.firefox.nightly",
include: ["mozilla", "firefox"],
exclude: []
),
Condition(
bundle_id: "com.logi.optionsplus",
include: ["logi"],
exclude: ["login", "logic"],
includeForce: []
),
Condition(
bundle_id: "commicrosoftvscode",
bundle_id: "com.microsoft.vscode",
include: ["vscode"],
exclude: [],
includeForce: ["\(home)/Library/Application Support/Code"]
includeForce: ["\(home)/Library/Application Support/Code/"]
),
Condition(
bundle_id: "comfacebookarchondeveloperid",
include: ["archonloginhelper"],
bundle_id: "com.facebook.archon.developerid",
include: ["archon.loginhelper"],
exclude: []
),
Condition(
bundle_id: "euexelbanstats",
bundle_id: "eu.exelban.stats",
include: [],
exclude: ["video"]
),
@@ -114,36 +138,59 @@ let skipConditions: [SkipCondition] = [
let skipReverse = ["apple", "temporary", "btserver", "proapps", "scripteditor", "ilife", "livefsd", "siritoday", "addressbook", "animoji", "appstore", "askpermission", "callhistory", "clouddocs", "diskimages", "dock", "facetime", "fileprovider", "instruments", "knowledge", "mobilesync", "syncservices", "homeenergyd", "icloud", "icdd", "networkserviceproxy", "familycircle", "geoservices", "installation", "passkit", "sharedimagecache", "desktop", "mbuseragent", "swiftpm", "baseband", "coresimulator", "photoslegacyupgrade", "photosupgrade", "siritts", "ipod", "globalpreferences", "apmanalytics", "apmexperiment", "avatarcache", "byhost", "contextstoreagent", "mobilemeaccounts", "intentbuilderc", "loginwindow", "momc", "replayd", "sharedfilelistd", "clang", "audiocomponent", "csexattrcryptoservice", "livetranscriptionagent", "sandboxhelper", "statuskitagent", "betaenrollmentd", "contentlinkingd", "diagnosticextensionsd", "gamed", "heard", "homed", "itunescloudd", "lldb", "mds", "mediaanalysisd", "metrickitd", "mobiletimerd", "proactived", "ptpcamerad", "studentd", "talagent", "watchlistd", "apptranslocation", "xcrun", "ds_store", "caches", "crashreporter", "trash", "pearcleaner", "amsdatamigratortool", "arfilecache", "assistant", "chromium", "cloudkit", "webkit", "databases", "diagnostic", "cache", "gamekit", "homebrew", "logi", "microsoft", "mozilla", "sync", "google", "sentinel", "hexnode", "sentry", "tvappservices", "reminders"]
// Function to load additional conditions from a GitHub JSON file
func loadConditionsFromGitHub() {
let url = URL(string: "https://api.github.com/repos/alienator88/Pearcleaner/contents/conditions.json")!
var request = URLRequest(url: url)
request.setValue("application/vnd.github.VERSION.raw", forHTTPHeaderField: "Accept")
request.setValue("2022-11-28", forHTTPHeaderField: "X-GitHub-Api-Version")
URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data {
do {
// Assuming the JSON structure directly maps to an array of Condition
let jsonObject = try JSONSerialization.jsonObject(with: data, options: [])
if let conditionArray = jsonObject as? [[String: Any]] {
let jsonData = try JSONSerialization.data(withJSONObject: conditionArray, options: [])
let additionalConditions = try JSONDecoder().decode([Condition].self, from: jsonData)
if !additionalConditions.isEmpty {
DispatchQueue.main.async {
conditions += additionalConditions
}
}
} else {
printOS("The data format is incorrect or empty for GitHub conditions processing.")
}
} catch {
printOS("Failed to decode conditions JSON: \(error.localizedDescription)")
}
} else {
printOS("Failed to fetch conditions data: \(error?.localizedDescription ?? "Unknown error")")
// Store and load conditions locally via SwiftData
class ConditionManager {
static let shared = ConditionManager()
private init() {
loadConditions()
}
// Function to save a condition
func saveCondition(_ condition: Condition) {
if condition.include.isEmpty && condition.exclude.isEmpty && (condition.includeForce?.isEmpty ?? true) {
deleteCondition(bundle_id: condition.bundle_id)
return
}
}.resume()
}
let defaults = UserDefaults.standard
let encoder = JSONEncoder()
let key = "Condition-\(condition.bundle_id)"
if let encoded = try? encoder.encode(condition) {
defaults.set(encoded, forKey: key)
conditions.append(condition)
}
}
// Function to delete a condition from defaults and conditions variable
func deleteCondition(bundle_id: String) {
let defaults = UserDefaults.standard
let key = "Condition-\(bundle_id.pearFormat())"
// Remove from UserDefaults
defaults.removeObject(forKey: key)
// Remove from conditions variable
conditions.removeAll { $0.bundle_id == bundle_id.pearFormat() }
}
// Function to load a condition and append to the global variable
func loadConditions() {
let defaults = UserDefaults.standard
let decoder = JSONDecoder()
for (key, value) in defaults.dictionaryRepresentation() {
if key.starts(with: "Condition-"), let savedCondition = value as? Data {
if let loadedCondition = try? decoder.decode(Condition.self, from: savedCondition) {
conditions.append(loadedCondition)
}
}
}
}
}
+1
View File
@@ -26,6 +26,7 @@ class Locations: ObservableObject {
self.tempDir = tempDir
self.apps = Category(name: "Apps", paths: [
"\(home)",
"\(home)/Library",
"\(home)/Library/Application Scripts",
"\(home)/Library/Application Support",
+14
View File
@@ -630,6 +630,20 @@ struct AnimatedSearchStyle: TextFieldStyle {
}
struct RoundedTextFieldStyle: TextFieldStyle {
func _body(configuration: TextField<Self._Label>) -> some View {
configuration
.padding(8)
.cornerRadius(6)
.textFieldStyle(.plain)
.overlay(
RoundedRectangle(cornerRadius: 8)
.strokeBorder(Color("mode").opacity(0.4), lineWidth: 0.8)
)
}
}
struct GlassEffect: NSViewRepresentable {
var material: NSVisualEffectView.Material // Choose the material you want
+4 -60
View File
@@ -52,10 +52,10 @@ func loadGithubReleases(appState: AppState, manual: Bool = false, releaseOnly: B
}.resume()
}
func checkForUpdate(appState: AppState, manual: Bool = false) {
func checkForUpdate(appState: AppState, manual: Bool) {
guard let latestRelease = appState.releases.first else { return }
let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
if latestRelease.tag_name > currentVersion ?? "" {
let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "0.0.0"
if latestRelease.tag_name > currentVersion {
updateOnMain {
appState.updateAvailable = true
}
@@ -198,14 +198,6 @@ enum UpdateFrequency: String, CaseIterable, Identifiable {
}
}
//func updateNextUpdateDate() {
// @AppStorage("settings.updater.updateFrequency") var updateFrequency: UpdateFrequency = .daily
// @AppStorage("settings.updater.nextUpdateDate") var nextUpdateDate = Date.now.timeIntervalSinceReferenceDate
//
// guard let updateInterval = updateFrequency.interval else { return }
// let newUpdateDate = Calendar.current.startOfDay(for: Date().addingTimeInterval(updateInterval))
// nextUpdateDate = newUpdateDate.timeIntervalSinceReferenceDate
//}
func checkAndUpdateIfNeeded(appState: AppState) {
@AppStorage("settings.updater.updateFrequency") var updateFrequency: UpdateFrequency = .daily
@@ -230,7 +222,7 @@ func checkAndUpdateIfNeeded(appState: AppState) {
func updateApp(appState: AppState) {
// Perform your update logic here
printOS("Updater: performing update")
printOS("Updater: checking for update")
loadGithubReleases(appState: appState)
}
@@ -243,54 +235,6 @@ func isSameDay(date1: Date, date2: Date) -> Bool {
return Calendar.current.isDate(date1, inSameDayAs: date2)
}
//func updateNextUpdateDate() {
// @AppStorage("settings.updater.updateTimeframe") var updateTimeframe: Int = 1
// @AppStorage("settings.updater.nextUpdateDate") var nextUpdateDate = Date.now.timeIntervalSinceReferenceDate
// let updateSeconds = updateTimeframe.daysToSeconds
// let newUpdateDate = Calendar.current.startOfDay(for: Date().addingTimeInterval(updateSeconds))
// nextUpdateDate = newUpdateDate.timeIntervalSinceReferenceDate
//}
//
//func checkAndUpdateIfNeeded(appState: AppState) {
// @AppStorage("settings.updater.updateTimeframe") var updateTimeframe: Int = 1
// @AppStorage("settings.updater.nextUpdateDate") var nextUpdateDate = Date.now.timeIntervalSinceReferenceDate
//
// let updateSeconds = updateTimeframe.daysToSeconds
// let now = Date()
//
// // Retrieve the next update date from UserDefaults
// let nextUpdateDateLocal = Date(timeIntervalSinceReferenceDate: nextUpdateDate)
//// let nextUpdateDate = UserDefaults.standard.object(forKey: "settings.updater.nextUpdateDate") as? Date
//
// // If there's no stored next update date or it's in the past, update immediately
// if !isSameDay(date1: nextUpdateDateLocal, date2: now) {
// // Next update date is in the future, no need to update
// printOS("Updater: next update date is in the future, skipping")
// return
// }
//
// // Update immediately and set next update date
// updateApp(appState: appState)
// setNextUpdateDate(interval: updateSeconds)
//}
//
//func updateApp(appState: AppState) {
// // Perform your update logic here
// printOS("Updater: performing update")
// loadGithubReleases(appState: appState)
//}
//
//func setNextUpdateDate(interval: TimeInterval) {
// let newUpdateDate = Calendar.current.startOfDay(for: Date().addingTimeInterval(interval))
// UserDefaults.standard.set(newUpdateDate.timeIntervalSinceReferenceDate, forKey: "settings.updater.nextUpdateDate")
//// UserDefaults.standard.set(newUpdateDate, forKey: "settings.updater.nextUpdateDate")
//}
//
//func isSameDay(date1: Date, date2: Date) -> Bool {
// return Calendar.current.isDate(date1, inSameDayAs: date2)
//}
// --- Updater Badge View
+25
View File
@@ -427,6 +427,31 @@ extension String {
}
}
// --- Returns comma separated string as array of strings
extension String {
func toConditionFormat() -> [String] {
if self.isEmpty {
return []
}
return self.components(separatedBy: ",").map { $0.trimmingCharacters(in: .whitespaces) }
}
}
// --- Overload the greater than operator ">" to do a semantic check on the string versions
extension String {
func versionStringToTuple() -> (Int, Int, Int) {
let components = self.split(separator: ".").compactMap { Int($0) }
return (components[0], components[1], components[2])
}
static func > (lhs: String, rhs: String) -> Bool {
let lhsVersion = lhs.versionStringToTuple()
let rhsVersion = rhs.versionStringToTuple()
return lhsVersion > rhsVersion
}
}
// --- Trash Relationship ---
//extension FileManager {