Skip to content

Commit fb87ae2

Browse files
committed
added custom location for android sdk in settings
1 parent e5e681a commit fb87ae2

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()
@@ -266,31 +269,44 @@ class AndroidEmulatorManager: ObservableObject {
266269
process.standardError = pipe
267270
process.arguments = arguments
268271

269-
// Find the command in common paths
270-
let possiblePaths = [
271-
"/usr/local/bin/\(command)",
272-
"/opt/homebrew/bin/\(command)",
273-
"/usr/bin/\(command)",
274-
"\(NSHomeDirectory())/Library/Android/sdk/emulator/\(command)",
275-
"\(NSHomeDirectory())/Library/Android/sdk/tools/bin/\(command)",
276-
"\(NSHomeDirectory())/Library/Android/sdk/cmdline-tools/latest/bin/\(command)",
277-
"\(NSHomeDirectory())/Library/Android/sdk/platform-tools/\(command)",
278-
"\(NSHomeDirectory())/Android/Sdk/emulator/\(command)",
279-
"\(NSHomeDirectory())/Android/Sdk/tools/bin/\(command)",
280-
"\(NSHomeDirectory())/Android/Sdk/cmdline-tools/latest/bin/\(command)",
281-
"\(NSHomeDirectory())/Android/Sdk/platform-tools/\(command)"
282-
]
283-
272+
// Get command path from SettingsManager first, then fallback to system paths
284273
var commandPath: String?
285-
for path in possiblePaths {
286-
if FileManager.default.fileExists(atPath: path) {
287-
commandPath = path
288-
break
274+
275+
switch command {
276+
case "emulator":
277+
commandPath = settingsManager.getEmulatorPath()
278+
if commandPath?.isEmpty == true {
279+
commandPath = nil
280+
}
281+
case "adb":
282+
commandPath = settingsManager.getADBPath()
283+
if commandPath?.isEmpty == true {
284+
commandPath = nil
285+
}
286+
case "avdmanager":
287+
commandPath = settingsManager.getAVDManagerPath()
288+
if commandPath?.isEmpty == true {
289+
commandPath = nil
290+
}
291+
default:
292+
// For system commands like ps, kill, etc., use system paths
293+
let possiblePaths = [
294+
"/usr/local/bin/\(command)",
295+
"/opt/homebrew/bin/\(command)",
296+
"/usr/bin/\(command)",
297+
"/bin/\(command)"
298+
]
299+
300+
for path in possiblePaths {
301+
if FileManager.default.fileExists(atPath: path) {
302+
commandPath = path
303+
break
304+
}
289305
}
290306
}
291307

292308
// If not found in specific paths, try using 'which' command
293-
if commandPath == nil {
309+
if commandPath == nil || commandPath == command {
294310
let whichProcess = Process()
295311
whichProcess.executableURL = URL(fileURLWithPath: "/usr/bin/which")
296312
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)