Skip to content

Commit 0f594f0

Browse files
committed
Merge branch 'main' of github.com:prudhvir3ddy/AvdManager-MacOS
2 parents 6149ca0 + fb87ae2 commit 0f594f0

File tree

5 files changed

+362
-26
lines changed

5 files changed

+362
-26
lines changed

avdmanager.xcodeproj/project.pbxproj

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
A45286472DF2180400CAD647 /* AndroidEmulatorManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A45286482DF2180400CAD647 /* AndroidEmulatorManager.swift */; };
1313
A45286492DF2180400CAD647 /* StatusBarManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A452864A2DF2180400CAD647 /* StatusBarManager.swift */; };
1414
A452864B2DF2180400CAD647 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A452864C2DF2180400CAD647 /* Assets.xcassets */; };
15-
15+
A452864F2DF2180400CAD647 /* SettingsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A45286502DF2180400CAD647 /* SettingsManager.swift */; };
16+
A45286512DF2180400CAD647 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A45286522DF2180400CAD647 /* SettingsView.swift */; };
1617
/* End PBXBuildFile section */
1718

1819
/* Begin PBXFileReference section */
@@ -23,6 +24,8 @@
2324
A452864A2DF2180400CAD647 /* StatusBarManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusBarManager.swift; sourceTree = "<group>"; };
2425
A452864C2DF2180400CAD647 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
2526
A452864E2DF2180500CAD647 /* avdmanager.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = avdmanager.entitlements; sourceTree = "<group>"; };
27+
A45286502DF2180400CAD647 /* SettingsManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsManager.swift; sourceTree = "<group>"; };
28+
A45286522DF2180400CAD647 /* SettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
2629
/* End PBXFileReference section */
2730

2831
/* Begin PBXFrameworksBuildPhase section */
@@ -59,6 +62,8 @@
5962
A45286462DF2180400CAD647 /* ContentView.swift */,
6063
A45286482DF2180400CAD647 /* AndroidEmulatorManager.swift */,
6164
A452864A2DF2180400CAD647 /* StatusBarManager.swift */,
65+
A45286502DF2180400CAD647 /* SettingsManager.swift */,
66+
A45286522DF2180400CAD647 /* SettingsView.swift */,
6267
A452864C2DF2180400CAD647 /* Assets.xcassets */,
6368
A452864E2DF2180500CAD647 /* avdmanager.entitlements */,
6469
);
@@ -138,6 +143,8 @@
138143
A45286452DF2180400CAD647 /* ContentView.swift in Sources */,
139144
A45286472DF2180400CAD647 /* AndroidEmulatorManager.swift in Sources */,
140145
A45286492DF2180400CAD647 /* StatusBarManager.swift in Sources */,
146+
A452864F2DF2180400CAD647 /* SettingsManager.swift in Sources */,
147+
A45286512DF2180400CAD647 /* SettingsView.swift in Sources */,
141148
);
142149
runOnlyForDeploymentPostprocessing = 0;
143150
};
@@ -268,11 +275,11 @@
268275
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
269276
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
270277
CODE_SIGN_ENTITLEMENTS = avdmanager/avdmanager.entitlements;
271-
CODE_SIGN_STYLE = Automatic;
278+
CODE_SIGN_STYLE = Manual;
272279
COMBINE_HIDPI_IMAGES = YES;
273280
CURRENT_PROJECT_VERSION = 1;
274281
DEVELOPMENT_ASSET_PATHS = "";
275-
DEVELOPMENT_TEAM = 6GSKHWTHQ7;
282+
DEVELOPMENT_TEAM = "";
276283
ENABLE_HARDENED_RUNTIME = YES;
277284
ENABLE_PREVIEWS = YES;
278285
GENERATE_INFOPLIST_FILE = YES;
@@ -286,6 +293,7 @@
286293
MARKETING_VERSION = 1.0;
287294
PRODUCT_BUNDLE_IDENTIFIER = com.prudhvi.avdmanager;
288295
PRODUCT_NAME = "$(TARGET_NAME)";
296+
PROVISIONING_PROFILE_SPECIFIER = "";
289297
SWIFT_EMIT_LOC_STRINGS = YES;
290298
SWIFT_VERSION = 5.0;
291299
};
@@ -297,11 +305,11 @@
297305
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
298306
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
299307
CODE_SIGN_ENTITLEMENTS = avdmanager/avdmanager.entitlements;
300-
CODE_SIGN_STYLE = Automatic;
308+
CODE_SIGN_STYLE = Manual;
301309
COMBINE_HIDPI_IMAGES = YES;
302310
CURRENT_PROJECT_VERSION = 1;
303311
DEVELOPMENT_ASSET_PATHS = "";
304-
DEVELOPMENT_TEAM = 6GSKHWTHQ7;
312+
DEVELOPMENT_TEAM = "";
305313
ENABLE_HARDENED_RUNTIME = YES;
306314
ENABLE_PREVIEWS = YES;
307315
GENERATE_INFOPLIST_FILE = YES;
@@ -315,6 +323,7 @@
315323
MARKETING_VERSION = 1.0;
316324
PRODUCT_BUNDLE_IDENTIFIER = com.prudhvi.avdmanager;
317325
PRODUCT_NAME = "$(TARGET_NAME)";
326+
PROVISIONING_PROFILE_SPECIFIER = "";
318327
SWIFT_EMIT_LOC_STRINGS = YES;
319328
SWIFT_VERSION = 5.0;
320329
};
@@ -344,4 +353,4 @@
344353
/* End XCConfigurationList section */
345354
};
346355
rootObject = A45286382DF2180300CAD647 /* Project object */;
347-
}
356+
}

avdmanager/AndroidEmulatorManager.swift

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ class AndroidEmulatorManager: ObservableObject {
2222
@Published var isLoading = false
2323

2424
private var cancellables = Set<AnyCancellable>()
25+
private var settingsManager: SettingsManager {
26+
return SettingsManager.shared
27+
}
2528

2629
init() {
2730
loadEmulators()
@@ -417,31 +420,44 @@ class AndroidEmulatorManager: ObservableObject {
417420
process.standardError = pipe
418421
process.arguments = arguments
419422

420-
// Find the command in common paths
421-
let possiblePaths = [
422-
"/usr/local/bin/\(command)",
423-
"/opt/homebrew/bin/\(command)",
424-
"/usr/bin/\(command)",
425-
"\(NSHomeDirectory())/Library/Android/sdk/emulator/\(command)",
426-
"\(NSHomeDirectory())/Library/Android/sdk/tools/bin/\(command)",
427-
"\(NSHomeDirectory())/Library/Android/sdk/cmdline-tools/latest/bin/\(command)",
428-
"\(NSHomeDirectory())/Library/Android/sdk/platform-tools/\(command)",
429-
"\(NSHomeDirectory())/Android/Sdk/emulator/\(command)",
430-
"\(NSHomeDirectory())/Android/Sdk/tools/bin/\(command)",
431-
"\(NSHomeDirectory())/Android/Sdk/cmdline-tools/latest/bin/\(command)",
432-
"\(NSHomeDirectory())/Android/Sdk/platform-tools/\(command)"
433-
]
434-
423+
// Get command path from SettingsManager first, then fallback to system paths
435424
var commandPath: String?
436-
for path in possiblePaths {
437-
if FileManager.default.fileExists(atPath: path) {
438-
commandPath = path
439-
break
425+
426+
switch command {
427+
case "emulator":
428+
commandPath = settingsManager.getEmulatorPath()
429+
if commandPath?.isEmpty == true {
430+
commandPath = nil
431+
}
432+
case "adb":
433+
commandPath = settingsManager.getADBPath()
434+
if commandPath?.isEmpty == true {
435+
commandPath = nil
436+
}
437+
case "avdmanager":
438+
commandPath = settingsManager.getAVDManagerPath()
439+
if commandPath?.isEmpty == true {
440+
commandPath = nil
441+
}
442+
default:
443+
// For system commands like ps, kill, etc., use system paths
444+
let possiblePaths = [
445+
"/usr/local/bin/\(command)",
446+
"/opt/homebrew/bin/\(command)",
447+
"/usr/bin/\(command)",
448+
"/bin/\(command)"
449+
]
450+
451+
for path in possiblePaths {
452+
if FileManager.default.fileExists(atPath: path) {
453+
commandPath = path
454+
break
455+
}
440456
}
441457
}
442458

443459
// If not found in specific paths, try using 'which' command
444-
if commandPath == nil {
460+
if commandPath == nil || commandPath == command {
445461
let whichProcess = Process()
446462
whichProcess.executableURL = URL(fileURLWithPath: "/usr/bin/which")
447463
whichProcess.arguments = [command]

avdmanager/SettingsManager.swift

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
//
2+
// SettingsManager.swift
3+
// avdmanager
4+
//
5+
// Created by Mekala Prudhvi Reddy on 06/06/25.
6+
//
7+
8+
import Foundation
9+
import Combine
10+
11+
class SettingsManager: ObservableObject {
12+
static let shared = SettingsManager()
13+
14+
@Published var androidSDKPath: String = ""
15+
16+
private let userDefaults = UserDefaults.standard
17+
private let androidSDKPathKey = "androidSDKPath"
18+
19+
private init() {
20+
loadSettings()
21+
}
22+
23+
private func loadSettings() {
24+
// Load Android SDK path from UserDefaults or auto-detect
25+
do {
26+
if let savedPath = userDefaults.string(forKey: androidSDKPathKey), !savedPath.isEmpty {
27+
androidSDKPath = savedPath
28+
} else {
29+
androidSDKPath = autoDetectAndroidSDK()
30+
}
31+
} catch {
32+
print("Error loading settings: \(error)")
33+
androidSDKPath = autoDetectAndroidSDK()
34+
}
35+
}
36+
37+
func saveSettings() {
38+
do {
39+
userDefaults.set(androidSDKPath, forKey: androidSDKPathKey)
40+
} catch {
41+
print("Error saving settings: \(error)")
42+
}
43+
}
44+
45+
func setAndroidSDKPath(_ path: String) {
46+
androidSDKPath = path.trimmingCharacters(in: .whitespacesAndNewlines)
47+
saveSettings()
48+
}
49+
50+
func validateAndroidSDKPath(_ path: String) -> Bool {
51+
let trimmedPath = path.trimmingCharacters(in: .whitespacesAndNewlines)
52+
guard !trimmedPath.isEmpty else { return false }
53+
54+
// Check if the path exists and contains expected Android SDK components
55+
let fileManager = FileManager.default
56+
57+
// Check for essential SDK directories/files
58+
let requiredPaths = [
59+
"\(trimmedPath)/emulator",
60+
"\(trimmedPath)/platform-tools",
61+
"\(trimmedPath)/tools",
62+
"\(trimmedPath)/cmdline-tools"
63+
]
64+
65+
// At least emulator and platform-tools should exist
66+
let emulatorExists = fileManager.fileExists(atPath: "\(trimmedPath)/emulator")
67+
let platformToolsExists = fileManager.fileExists(atPath: "\(trimmedPath)/platform-tools")
68+
69+
return emulatorExists || platformToolsExists
70+
}
71+
72+
func autoDetectAndroidSDK() -> String {
73+
let possiblePaths = [
74+
"\(NSHomeDirectory())/Library/Android/sdk",
75+
"\(NSHomeDirectory())/Android/Sdk",
76+
"/usr/local/android-sdk",
77+
"/opt/android-sdk",
78+
"/Applications/Android\\ Studio.app/Contents/android-sdk"
79+
]
80+
81+
for path in possiblePaths {
82+
if validateAndroidSDKPath(path) {
83+
return path
84+
}
85+
}
86+
87+
return ""
88+
}
89+
90+
func getEmulatorPath() -> String {
91+
guard !androidSDKPath.isEmpty else { return "emulator" }
92+
return "\(androidSDKPath)/emulator/emulator"
93+
}
94+
95+
func getADBPath() -> String {
96+
guard !androidSDKPath.isEmpty else { return "adb" }
97+
return "\(androidSDKPath)/platform-tools/adb"
98+
}
99+
100+
func getAVDManagerPath() -> String {
101+
guard !androidSDKPath.isEmpty else { return "avdmanager" }
102+
103+
// Try different possible locations
104+
let possiblePaths = [
105+
"\(androidSDKPath)/cmdline-tools/latest/bin/avdmanager",
106+
"\(androidSDKPath)/tools/bin/avdmanager"
107+
]
108+
109+
for path in possiblePaths {
110+
if FileManager.default.fileExists(atPath: path) {
111+
return path
112+
}
113+
}
114+
115+
return "avdmanager"
116+
}
117+
118+
func resetToDefaults() {
119+
androidSDKPath = autoDetectAndroidSDK()
120+
saveSettings()
121+
}
122+
}

0 commit comments

Comments
 (0)