From 8e87b4a5edc7298a18e3aaf0275192b849295a3d Mon Sep 17 00:00:00 2001 From: Dina Basumatary Date: Tue, 14 Nov 2023 20:30:11 +1100 Subject: [PATCH 1/4] add pinnediOSSimulators and pinnedAndroidEmulators in UserDefaults --- MiniSim/Extensions/UserDefaults+Configuration.swift | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/MiniSim/Extensions/UserDefaults+Configuration.swift b/MiniSim/Extensions/UserDefaults+Configuration.swift index 18d2791..e000f53 100644 --- a/MiniSim/Extensions/UserDefaults+Configuration.swift +++ b/MiniSim/Extensions/UserDefaults+Configuration.swift @@ -15,6 +15,8 @@ extension UserDefaults { static let isOnboardingFinished = "isOnboardingFinished" static let enableiOSSimulators = "enableiOSSimulators" static let enableAndroidEmulators = "enableAndroidEmulators" + static let pinnediOSSimulators = "pinnediOSSimulators" + static let pinnedAndroidEmulators = "pinnedAndroidEmulators" } @objc dynamic public var androidHome: String? { @@ -46,4 +48,15 @@ extension UserDefaults { get { bool(forKey: Keys.enableAndroidEmulators) } set { set(newValue, forKey: Keys.enableAndroidEmulators) } } + + public var pinnediOSSimulators: [String]? { + get { array(forKey: Keys.pinnediOSSimulators) as? [String] } + set { set(newValue, forKey: Keys.pinnediOSSimulators) } + } + + public var pinnedAndroidEmulators: [String]? { + get { array(forKey: Keys.pinnedAndroidEmulators) as? [String] } + set { set(newValue, forKey: Keys.pinnedAndroidEmulators) } + } + } From fce64dc06ad7a60489d695f5c8ec902497a77e73 Mon Sep 17 00:00:00 2001 From: Dina Basumatary Date: Tue, 14 Nov 2023 20:30:42 +1100 Subject: [PATCH 2/4] add pinned field to Device --- MiniSim/Model/Device.swift | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/MiniSim/Model/Device.swift b/MiniSim/Model/Device.swift index 5b34cd2..0771294 100644 --- a/MiniSim/Model/Device.swift +++ b/MiniSim/Model/Device.swift @@ -11,30 +11,33 @@ struct Device: Hashable, Codable { var ID: String? var booted: Bool = false var platform: Platform + var pinned: Bool var displayName: String { + let pinIcon = pinned ? " 📌" : "" switch platform { case .ios: if let version { - return "\(name) - (\(version))" + return "\(name) - (\(version))" + pinIcon } - return name + return name + pinIcon case .android: - return name + return name + pinIcon } } enum CodingKeys: String, CodingKey { - case name, version, ID, booted, platform, displayName + case name, version, ID, booted, platform, displayName, pinned } - init(name: String, version: String? = nil, ID: String?, booted: Bool = false, platform: Platform) { + init(name: String, version: String? = nil, ID: String?, booted: Bool = false, platform: Platform, pinned: Bool = false) { self.name = name self.version = version self.ID = ID self.booted = booted self.platform = platform + self.pinned = pinned } init(from decoder: Decoder) throws { @@ -44,6 +47,7 @@ struct Device: Hashable, Codable { ID = try values.decode(String.self, forKey: .ID) booted = try values.decode(Bool.self, forKey: .booted) platform = try values.decode(Platform.self, forKey: .platform) + pinned = try values.decode(Bool.self, forKey: .pinned) } func encode(to encoder: Encoder) throws { From 18f584862eedefa29195d8f6725966430ffe4d37 Mon Sep 17 00:00:00 2001 From: Dina Basumatary Date: Tue, 14 Nov 2023 20:37:05 +1100 Subject: [PATCH 3/4] add submenu item to toggle pinned state --- MiniSim/MenuItems/SubMenuItem.swift | 14 +++++++++++ MiniSim/Service/DeviceService.swift | 37 ++++++++++++++++++++++++++--- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/MiniSim/MenuItems/SubMenuItem.swift b/MiniSim/MenuItems/SubMenuItem.swift index c8264a2..788f127 100644 --- a/MiniSim/MenuItems/SubMenuItem.swift +++ b/MiniSim/MenuItems/SubMenuItem.swift @@ -27,6 +27,7 @@ enum SubMenuItems { case toggleA11y case paste case delete + case togglePinned case customCommand = 200 } @@ -109,6 +110,17 @@ enum SubMenuItems { ) } + struct TogglePinToTop: SubMenuActionItem { + let title = NSLocalizedString("Pin/unpin to top", comment: "") + let tag = Tags.togglePinned.rawValue + let bootsDevice = false + let needBootedDevice = false + let image = NSImage( + systemSymbolName: "pin", + accessibilityDescription: "Pin or unpin to Top" + ) + } + struct Delete: SubMenuActionItem { let title = NSLocalizedString("Delete simulator", comment: "") let tag = Tags.delete.rawValue @@ -128,6 +140,7 @@ extension SubMenuItems { Separator(), + TogglePinToTop(), ColdBoot(), NoAudio(), ToggleA11y(), @@ -140,6 +153,7 @@ extension SubMenuItems { Separator(), + TogglePinToTop(), Delete() ] } diff --git a/MiniSim/Service/DeviceService.swift b/MiniSim/Service/DeviceService.swift index 6058c87..bfbdcd6 100644 --- a/MiniSim/Service/DeviceService.swift +++ b/MiniSim/Service/DeviceService.swift @@ -208,6 +208,30 @@ class DeviceService: DeviceServiceProtocol { } } } + + static func togglePinned(device: Device) { + var pinnedDevices: [String]? + switch device.platform { + case .android: + pinnedDevices = UserDefaults.standard.pinnedAndroidEmulators + var dedupedPinnedDevices = Set(pinnedDevices ?? []) + if dedupedPinnedDevices.contains(device.name) { + dedupedPinnedDevices.remove(device.name) + } else { + dedupedPinnedDevices.insert(device.name) + } + UserDefaults.standard.pinnedAndroidEmulators = Array(dedupedPinnedDevices) + case .ios: + pinnedDevices = UserDefaults.standard.pinnediOSSimulators + var dedupedPinnedDevices = Set(pinnedDevices ?? []) + if dedupedPinnedDevices.contains(device.name) { + dedupedPinnedDevices.remove(device.name) + } else { + dedupedPinnedDevices.insert(device.name) + } + UserDefaults.standard.pinnediOSSimulators = Array(dedupedPinnedDevices) + } + } } // MARK: iOS Methods @@ -216,18 +240,21 @@ extension DeviceService { static private func parseIOSDevices(result: [String]) -> [Device] { var devices: [Device] = [] var osVersion = "" + let pinnediOSSimulators: [String] = UserDefaults.standard.pinnediOSSimulators ?? [] result.forEach { line in if let currentOs = line.match("-- (.*?) --").first, currentOs.count > 0 { osVersion = currentOs[1] } if let device = line.match("(.*?) (\\(([0-9.]+)\\) )?\\(([0-9A-F-]+)\\) (\\(.*?)\\)").first { + let deviceName = device[1].trimmingCharacters(in: .whitespacesAndNewlines) devices.append( Device( - name: device[1].trimmingCharacters(in: .whitespacesAndNewlines), + name: deviceName, version: osVersion, ID: device[4], booted: device[5].contains("Booted"), - platform: .ios + platform: .ios, + pinned: pinnediOSSimulators.contains(deviceName) ) ) } @@ -295,6 +322,9 @@ extension DeviceService { NSPasteboard.general.copyToPasteboard(text: deviceID) DeviceService.showSuccessMessage(title: "Device ID copied to clipboard!", message: deviceID) } + case .togglePinned: + DeviceService.togglePinned(device: device) + case .delete: guard let deviceID = device.ID else { return } if !NSAlert.showQuestionDialog(title: "Are you sure?", message: "Are you sure you want to delete this Simulator?") { @@ -366,10 +396,11 @@ extension DeviceService { let adbPath = try ADB.getAdbPath() let output = try shellOut(to: emulatorPath, arguments: ["-list-avds"]) let splitted = output.components(separatedBy: "\n") + let pinnedAndroidEmulators: [String] = UserDefaults.standard.pinnedAndroidEmulators ?? [] return splitted.filter({ !$0.isEmpty }).map { let adbId = try? ADB.getAdbId(for: $0, adbPath: adbPath) - return Device(name: $0, ID: adbId, booted: adbId != nil, platform: .android) + return Device(name: $0, ID: adbId, booted: adbId != nil, platform: .android, pinned: pinnedAndroidEmulators.contains($0)) } } From dd8917c319b5a2cde2bdcf9a07633e3664d1b8e8 Mon Sep 17 00:00:00 2001 From: Dina Basumatary Date: Wed, 15 Nov 2023 07:42:42 +1100 Subject: [PATCH 4/4] Update DeviceService.swift --- MiniSim/Service/DeviceService.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MiniSim/Service/DeviceService.swift b/MiniSim/Service/DeviceService.swift index bfbdcd6..9d81379 100644 --- a/MiniSim/Service/DeviceService.swift +++ b/MiniSim/Service/DeviceService.swift @@ -451,6 +451,9 @@ extension DeviceService { NSPasteboard.general.copyToPasteboard(text: device.name) DeviceService.showSuccessMessage(title: "Device name copied to clipboard!", message: device.name) + case .togglePinned: + DeviceService.togglePinned(device: device) + case .paste: guard let clipboard = NSPasteboard.general.pasteboardItems?.first?.string(forType: .string) else { break } try DeviceService.sendText(device: device, text: clipboard)