diff --git a/Geranium.xcodeproj/project.pbxproj b/Geranium.xcodeproj/project.pbxproj index a29b993..31c8b47 100644 --- a/Geranium.xcodeproj/project.pbxproj +++ b/Geranium.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ 7989FD082B34894400FBAB8D /* BookMarkSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7989FD072B34894400FBAB8D /* BookMarkSlider.swift */; }; 7989FD0A2B348A0200FBAB8D /* BookMarkHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7989FD092B348A0200FBAB8D /* BookMarkHelper.swift */; }; 799488A62B347E2C00AFFDCF /* LocSimManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799488A52B347E2C00AFFDCF /* LocSimManager.swift */; }; + 799615442B34A1930053558E /* DaemonWelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799615432B34A1930053558E /* DaemonWelcomeView.swift */; }; 79AEFBBD2B2CBC6B0074EC34 /* ProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79AEFBBC2B2CBC6B0074EC34 /* ProgressBar.swift */; }; 79BA958A2B30D91600380D83 /* RootHelperMan.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79BA95892B30D91600380D83 /* RootHelperMan.swift */; }; 79D359942B2C9D91004C0FCF /* LSApplicationProxy+AltList.m in Sources */ = {isa = PBXBuildFile; fileRef = 79D359932B2C9D91004C0FCF /* LSApplicationProxy+AltList.m */; }; @@ -68,6 +69,7 @@ 7989FD092B348A0200FBAB8D /* BookMarkHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookMarkHelper.swift; sourceTree = ""; }; 799488A52B347E2C00AFFDCF /* LocSimManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocSimManager.swift; sourceTree = ""; }; 799488A72B347E2F00AFFDCF /* LocSimPrivateHeaders.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LocSimPrivateHeaders.h; sourceTree = ""; }; + 799615432B34A1930053558E /* DaemonWelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DaemonWelcomeView.swift; sourceTree = ""; }; 79AEFBBC2B2CBC6B0074EC34 /* ProgressBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressBar.swift; sourceTree = ""; }; 79BA95892B30D91600380D83 /* RootHelperMan.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootHelperMan.swift; sourceTree = ""; }; 79D359932B2C9D91004C0FCF /* LSApplicationProxy+AltList.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "LSApplicationProxy+AltList.m"; sourceTree = ""; }; @@ -253,6 +255,7 @@ isa = PBXGroup; children = ( FAA457282B26668900FC7287 /* DaemonView.swift */, + 799615432B34A1930053558E /* DaemonWelcomeView.swift */, 792845592B2C80A40021E1FD /* DaemonDisabler.swift */, ); path = DaemonMan; @@ -423,6 +426,7 @@ 792845532B2C67910021E1FD /* FileCleaner.swift in Sources */, 799488A62B347E2C00AFFDCF /* LocSimManager.swift in Sources */, FAA456D72B265FEA00FC7287 /* ContentView.swift in Sources */, + 799615442B34A1930053558E /* DaemonWelcomeView.swift in Sources */, 7918209A2B31F109007EEC24 /* LogHelper.swift in Sources */, 7928455A2B2C80A40021E1FD /* DaemonDisabler.swift in Sources */, 7989FD0A2B348A0200FBAB8D /* BookMarkHelper.swift in Sources */, diff --git a/Geranium.xcodeproj/project.xcworkspace/xcuserdata/cclerc.xcuserdatad/UserInterfaceState.xcuserstate b/Geranium.xcodeproj/project.xcworkspace/xcuserdata/cclerc.xcuserdatad/UserInterfaceState.xcuserstate index 7f5c2b6..9cfb327 100644 Binary files a/Geranium.xcodeproj/project.xcworkspace/xcuserdata/cclerc.xcuserdatad/UserInterfaceState.xcuserstate and b/Geranium.xcodeproj/project.xcworkspace/xcuserdata/cclerc.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Geranium/DaemonMan/DaemonView.swift b/Geranium/DaemonMan/DaemonView.swift index 6453a01..9729d56 100644 --- a/Geranium/DaemonMan/DaemonView.swift +++ b/Geranium/DaemonMan/DaemonView.swift @@ -17,6 +17,9 @@ struct DaemonView: View { @State private var searchText = "" @State private var processes: [ProcessItem] = [] @State private var timer: Timer? + @State private var toDisable: [String] = [] + @State private var toEnable: [String] = [] + @AppStorage("isDaemonFirstRun") var isDaemonFirstRun: Bool = true var filteredProcesses: [ProcessItem] { processes.filter { @@ -46,18 +49,23 @@ struct DaemonView: View { ForEach(filteredProcesses) { process in HStack { Text("PID: \(process.pid)") + .foregroundColor(toDisable.contains(process.procName) ? .red : .primary) Spacer() Text("Name: \(process.procName)") + .foregroundColor(toDisable.contains(process.procName) ? .red : .primary) } } .onDelete { indexSet in guard let index = indexSet.first else { return } - let process = filteredProcesses[index] - //MARK: Probably the WORST EVER WAY to define a daemon's bundle ID. I'll try over objc s0n - daemonManagement(key: "com.apple.\(process.procName)",value: true, plistPath: "/var/db/com.apple.xpc.launchd/disabled.plist") - killall(process.procName) - updateProcesses() - UIApplication.shared.alert(title: "\(process.procName) was successfuly disabled in launchd database", body: "This daemon won't launch next startup.") + let process = processes[index] + + if let indexToRemove = toDisable.firstIndex(of: process.procName) { + toDisable.remove(at: indexToRemove) + updateProcesses() + } else { + toDisable.append(process.procName) + updateProcesses() + } } } .toolbar{ @@ -66,6 +74,24 @@ struct DaemonView: View { .font(.title2) .bold() } + ToolbarItem(placement: .navigationBarTrailing) { + Button(action: { + //MARK: Probably the WORST EVER WAY to define a daemon's bundle ID. I'll try over objc s0n + for process in toDisable { + UIApplication.shared.confirmAlert(title: "Are you sure you want to disable \(process) ?", body: "You can't undo this action. Your phone might bootloop.", onOK: { + daemonManagement(key: "com.apple.\(process)", value: true, plistPath: "/var/db/com.apple.xpc.launchd/disabled.plist") + }, noCancel: false) + } + if toDisable == [] { + UIApplication.shared.alert(title: "You didn't select any daemon", body: "Please swipe a running daemon to the left to disable it.") + } + }) { + Image(systemName: "checkmark") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 24, height: 24) + } + } } .onAppear { startTimer() @@ -73,6 +99,17 @@ struct DaemonView: View { .onDisappear { stopTimer() } + .sheet(isPresented: $isDaemonFirstRun) { + if #available(iOS 16.0, *) { + NavigationStack { + DaemonWelcomeView() + } + } else { + NavigationView { + DaemonWelcomeView() + } + } + } } private func startTimer() { diff --git a/Geranium/DaemonMan/DaemonWelcomeView.swift b/Geranium/DaemonMan/DaemonWelcomeView.swift new file mode 100644 index 0000000..5265ec8 --- /dev/null +++ b/Geranium/DaemonMan/DaemonWelcomeView.swift @@ -0,0 +1,43 @@ +// +// DaemonWelcomeView.swift +// Geranium +// +// Created by cclerc on 21.12.23. +// + +import SwiftUI + +struct DaemonWelcomeView: View { + @Environment(\.dismiss) var dismiss + var body: some View { + List { + + Section(header: Text("Purpose")) { + Text("By adding some rules to iOS internals, we prevent the daemon from launching when you boot your phone. The app list every running daemon, and you can swipe left to remove them.") + } + + Section(header: Text("WARNING")) { + Text("DISABLING SYSTEM DAEMONS IS NOT RECOMMENDED. YOU COULD BOOTLOOP YOUR DEVICE. PROCEED WITH CAUTION. I AM NOT RESPONSIBLE FOR ANY PROBLEM ON YOUR DEVICE.") + .foregroundStyle(.red) + } + Section(header: Text("Please note")) { + Text("If you missclick and accidently remove a daemon, you can still re-enable it by swipping again (should still be Delete). Edit won't be applied until you hit the apply button top left.") + .foregroundStyle(.green) + } + } + .navigationTitle("Notice") + .navigationBarItems(trailing: Button("Understood") { + close() + }) + .environment(\.defaultMinListRowHeight, 50) + .interactiveDismissDisabled() + } + + func close() { + dismiss() + } +} + +#Preview { + DaemonWelcomeView() +} diff --git a/Geranium/LocSim/BookMark/BookMarkHelper.swift b/Geranium/LocSim/BookMark/BookMarkHelper.swift index 05cefe3..55fd0c8 100644 --- a/Geranium/LocSim/BookMark/BookMarkHelper.swift +++ b/Geranium/LocSim/BookMark/BookMarkHelper.swift @@ -6,6 +6,7 @@ // import Foundation +import SwiftUI func BookMarkSave(lat: Double, long: Double, name: String) -> Bool { let bookmark: [String: Any] = ["name": name, "lat": lat, "long": long] @@ -22,3 +23,57 @@ func BookMarkRetrieve() -> [[String: Any]] { return [] } } + +func isThereAnyMika() -> Bool { + let plistPath = "/var/mobile/Library/Preferences/com.mika.LocationSimulation.plist" + guard FileManager.default.fileExists(atPath: plistPath) else { + return false + } + + do { + let data = try Data(contentsOf: URL(fileURLWithPath: plistPath)) + var format: PropertyListSerialization.PropertyListFormat = .xml + let plist = try PropertyListSerialization.propertyList(from: data, options: .mutableContainersAndLeaves, format: &format) + guard let plistDict = plist as? [String: Any] else { + print("Plist is not a dictionary.") + return false + } + if let datasArray = plistDict["datas"] as? [Any] { + return true + } else { + return false + } + + } catch { + return false + } +} + + +func importMika() { + let plistPath = "/var/mobile/Library/Preferences/com.mika.LocationSimulation.plist" + + do { + let data = try Data(contentsOf: URL(fileURLWithPath: plistPath)) + var format: PropertyListSerialization.PropertyListFormat = .xml + let plist = try PropertyListSerialization.propertyList(from: data, options: .mutableContainersAndLeaves, format: &format) + guard let plistDict = plist as? [String: Any] else { + print("not a dictionary") + return + } + if let datasArray = plistDict["datas"] as? [[String: Any]] { + for dataDict in datasArray { + if let la = dataDict["la"] as? Double, + let lo = dataDict["lo"] as? Double, + let remark = dataDict["remark"] as? String { + BookMarkSave(lat: la, long: lo, name: remark) + } + } + } else { + print("datas not there") + } + UIApplication.shared.alert(title:"Done !",body:"Your saved bookmarks/records from Mika's LocSim were imported.") + } catch { + print("cant read file \(error)") + } +} diff --git a/Geranium/LocSim/BookMark/BookMarkSlider.swift b/Geranium/LocSim/BookMark/BookMarkSlider.swift index a5d546b..281b27a 100644 --- a/Geranium/LocSim/BookMark/BookMarkSlider.swift +++ b/Geranium/LocSim/BookMark/BookMarkSlider.swift @@ -20,6 +20,7 @@ struct BookMarkSlider: View { @Binding var long: Double @State private var name = "" @State private var result: Bool = false + @AppStorage("isMika") var isMika: Bool = false @State private var bookmarks: [Bookmark] = BookMarkRetrieve().map { Bookmark(name: $0["name"] as! String, lat: $0["lat"] as! Double, long: $0["long"] as! Double) } @@ -96,6 +97,17 @@ struct BookMarkSlider: View { } , alignment: .center ) + .onAppear { + if isThereAnyMika(), !isMika { + UIApplication.shared.confirmAlert(title:"Mika's LocSim bookmarks/records detected.", body: "Do you want to import them ?", onOK: { + importMika() + bookmarks = BookMarkRetrieve().map { + Bookmark(name: $0["name"] as! String, lat: $0["lat"] as! Double, long: $0["long"] as! Double) + } + isMika.toggle() + }, noCancel: false, yes: true) + } + } } } diff --git a/Geranium/WelcomeView.swift b/Geranium/WelcomeView.swift index ba421d4..962fb90 100644 --- a/Geranium/WelcomeView.swift +++ b/Geranium/WelcomeView.swift @@ -41,13 +41,11 @@ struct WelcomeView: View { } } .navigationTitle("Welcome!") + .navigationBarItems(trailing: Button("Dismiss") { + close() + }) .environment(\.defaultMinListRowHeight, 50) .interactiveDismissDisabled() - Button("Dismiss") { - close() - } - .buttonStyle(DangerButtonStyle()) - .padding() } func close() {