Skip to content

Commit de6d0a5

Browse files
committed
Work on the Android SDK
1 parent 9bfc637 commit de6d0a5

File tree

2 files changed

+871
-3
lines changed

2 files changed

+871
-3
lines changed

src/android/android-sdk.ts

Lines changed: 230 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,43 @@ import { createHash } from 'node:crypto';
44
import { tailgate } from '../util/tailgate.js';
55
import snooplogg from 'snooplogg';
66
import { isDir } from '../util/is-dir.js';
7+
import { join } from 'node:path';
8+
import { readPropertiesFile } from './util/read-properties-file.js';
9+
import { isFile } from '../util/is-file.js';
10+
import { readdir } from 'node:fs/promises';
711

812
const { error, log } = snooplogg('android:sdk');
913

14+
const bat = process.platform === 'win32' ? '.bat' : '';
15+
const exe = process.platform === 'win32' ? '.exe' : '';
16+
17+
interface AndroidSDKExecutables {
18+
adb: string;
19+
android: string;
20+
emulator: string;
21+
}
22+
1023
export class AndroidSDK {
11-
constructor() {
12-
//
24+
buildTools: Record<string, string>;
25+
executables: AndroidSDKExecutables;
26+
path: string;
27+
version: string;
28+
29+
constructor({
30+
buildTools,
31+
executables,
32+
path,
33+
version
34+
}: {
35+
buildTools: Record<string, string>;
36+
executables: AndroidSDKExecutables;
37+
path: string;
38+
version: string;
39+
}) {
40+
this.buildTools = buildTools;
41+
this.executables = executables;
42+
this.path = path;
43+
this.version = version;
1344
}
1445

1546
static async load(path: string): Promise<AndroidSDK> {
@@ -21,7 +52,192 @@ export class AndroidSDK {
2152
throw new Error('Android SDK path does not exist: ${path}');
2253
}
2354

24-
return new AndroidSDK();
55+
const executables = {
56+
adb: join(path, 'platform-tools', `adb${exe}`),
57+
android: join(path, 'tools', `android${bat}`),
58+
emulator: join(path, 'tools', `emulator${exe}`),
59+
};
60+
61+
const buildTools: Record<string, string> = {};
62+
for (const ver of await readdir(join(path, 'build-tools'))) {
63+
const dir = join(path, 'build-tools', ver);
64+
if (isFile(join(dir, 'source.properties'))) {
65+
buildTools[ver] = dir;
66+
}
67+
}
68+
69+
const systemImagesDir = join(path, 'system-images');
70+
71+
// /**
72+
// * Detect system images
73+
// */
74+
// const systemImagesDir = path.join(dir, 'system-images');
75+
// if (isDir(systemImagesDir)) {
76+
// for (const platform of fs.readdirSync(systemImagesDir)) {
77+
// const platformDir = path.join(systemImagesDir, platform);
78+
// if (isDir(platformDir)) {
79+
// for (const tag of fs.readdirSync(platformDir)) {
80+
// const tagDir = path.join(platformDir, tag);
81+
// if (isDir(tagDir)) {
82+
// for (const abi of fs.readdirSync(tagDir)) {
83+
// const abiDir = path.join(tagDir, abi);
84+
// const props = readPropertiesFile(path.join(abiDir, 'source.properties'));
85+
// if (props && props['AndroidVersion.ApiLevel'] && props['SystemImage.TagId'] && props['SystemImage.Abi']) {
86+
// const imageDir = path.relative(systemImagesDir, abiDir).replace(/\\/g, '/');
87+
// const skinsDir = path.join(abiDir, 'skins');
88+
89+
// this.systemImages[imageDir] = {
90+
// abi: props['SystemImage.Abi'],
91+
// sdk: `android-${props['AndroidVersion.CodeName'] || props['AndroidVersion.ApiLevel']}`,
92+
// skins: isDir(skinsDir) ? fs.readdirSync(skinsDir).map(name => {
93+
// return isFile(path.join(skinsDir, name, 'hardware.ini')) ? name : null;
94+
// }).filter(x => x) : [],
95+
// type: props['SystemImage.TagId']
96+
// };
97+
// }
98+
// }
99+
// }
100+
// }
101+
// }
102+
// }
103+
// }
104+
105+
// /**
106+
// * Detect platforms
107+
// */
108+
// const platformsDir = path.join(dir, 'platforms');
109+
// if (isDir(platformsDir)) {
110+
// for (const name of fs.readdirSync(platformsDir)) {
111+
// const dir = path.join(platformsDir, name);
112+
// const sourceProps = readPropertiesFile(path.join(dir, 'source.properties'));
113+
// const apiLevel = sourceProps ? ~~sourceProps['AndroidVersion.ApiLevel'] : null;
114+
// if (!sourceProps || !apiLevel || !isFile(path.join(dir, 'build.prop'))) {
115+
// continue;
116+
// }
117+
118+
// // read in the sdk properties, if exists
119+
// const sdkProps = readPropertiesFile(path.join(dir, 'sdk.properties'));
120+
121+
// // detect the available skins
122+
// const skinsDir = path.join(dir, 'skins');
123+
// const skins = isDir(skinsDir) ? fs.readdirSync(skinsDir).map(name => {
124+
// return isFile(path.join(skinsDir, name, 'hardware.ini')) ? name : null;
125+
// }).filter(x => x) : [];
126+
// let defaultSkin = sdkProps && sdkProps['sdk.skin.default'];
127+
// if (skins.indexOf(defaultSkin) === -1 && skins.indexOf(defaultSkin = 'WVGA800') === -1) {
128+
// defaultSkin = skins[skins.length - 1] || null;
129+
// }
130+
131+
// const apiName = sourceProps['AndroidVersion.CodeName'] || apiLevel;
132+
// const sdk = `android-${apiName}`;
133+
// let tmp;
134+
135+
// const abis = {};
136+
// for (const image of Object.values(this.systemImages)) {
137+
// if (image.sdk === sdk) {
138+
// if (!abis[image.type]) {
139+
// abis[image.type] = [];
140+
// }
141+
// if (!abis[image.type].includes(image.abi)) {
142+
// abis[image.type].push(image.abi);
143+
// }
144+
145+
// for (const skin of image.skins) {
146+
// if (!skins.includes(skin)) {
147+
// skins.push(skin);
148+
// }
149+
// }
150+
// }
151+
// }
152+
153+
// this.platforms.push({
154+
// abis: abis,
155+
// aidl: isFile(tmp = path.join(dir, 'framework.aidl')) ? tmp : null,
156+
// androidJar: isFile(tmp = path.join(dir, 'android.jar')) ? tmp : null,
157+
// apiLevel: apiLevel,
158+
// codename: sourceProps['AndroidVersion.CodeName'] || null,
159+
// defaultSkin: defaultSkin,
160+
// minToolsRev: +sourceProps['Platform.MinToolsRev'] || null,
161+
// name: `Android ${sourceProps['Platform.Version']}${sourceProps['AndroidVersion.CodeName'] ? ' (Preview)' : ''}`,
162+
// path: dir,
163+
// revision: +sourceProps['Layoutlib.Revision'] || null,
164+
// sdk,
165+
// skins: skins,
166+
// version: sourceProps['Platform.Version']
167+
// });
168+
// }
169+
// }
170+
171+
// /**
172+
// * Detect addons
173+
// */
174+
// const addonsDir = path.join(dir, 'add-ons');
175+
// if (isDir(addonsDir)) {
176+
// for (const name of fs.readdirSync(addonsDir)) {
177+
// const dir = path.join(addonsDir, name);
178+
// const props = readPropertiesFile(path.join(dir, 'source.properties')) || readPropertiesFile(path.join(dir, 'manifest.ini'));
179+
// if (!props) {
180+
// continue;
181+
// }
182+
183+
// const apiLevel = parseInt(props['AndroidVersion.ApiLevel'] || props.api);
184+
// const vendorDisplay = props['Addon.VendorDisplay'] || props.vendor;
185+
// const nameDisplay = props['Addon.NameDisplay'] || props.name;
186+
// if (!apiLevel || isNaN(apiLevel) || !vendorDisplay || !nameDisplay) {
187+
// continue;
188+
// }
189+
190+
// let basedOn = null;
191+
// for (const platform of this.platforms) {
192+
// if (platform.codename === null && platform.apiLevel === apiLevel) {
193+
// basedOn = platform;
194+
// break;
195+
// }
196+
// }
197+
198+
// this.addons.push({
199+
// abis: basedOn && basedOn.abis || null,
200+
// aidl: basedOn && basedOn.aidl || null,
201+
// androidJar: basedOn && basedOn.androidJar || null,
202+
// apiLevel: apiLevel,
203+
// basedOn: basedOn ? { version: basedOn.version, apiLevel: basedOn.apiLevel } : null,
204+
// codename: props['AndroidVersion.CodeName'] || props.codename || null,
205+
// defaultSkin: basedOn && basedOn.defaultSkin || null,
206+
// description: props['Pkg.Desc'] || props.description || null,
207+
// minToolsRev: basedOn && basedOn.minToolsRev || null,
208+
// name: nameDisplay,
209+
// path: dir,
210+
// revision: parseInt(props['Pkg.Revision'] || props.revision) || null,
211+
// sdk: `${vendorDisplay}:${nameDisplay}:${apiLevel}`,
212+
// skins: basedOn && basedOn.skins || null,
213+
// vendor: vendorDisplay,
214+
// version: basedOn && basedOn.version || null
215+
// });
216+
// }
217+
// }
218+
219+
// function sortFn(a, b) {
220+
// if (a.codename === null) {
221+
// if (b.codename !== null && a.apiLevel === b.apiLevel) {
222+
// // sort GA releases before preview releases
223+
// return -1;
224+
// }
225+
// } else if (a.apiLevel === b.apiLevel) {
226+
// return b.codename === null ? 1 : a.codename.localeCompare(b.codename);
227+
// }
228+
229+
// return a.apiLevel - b.apiLevel;
230+
// }
231+
232+
// this.platforms.sort(sortFn);
233+
// this.addons.sort(sortFn);
234+
235+
return new AndroidSDK({
236+
buildTools,
237+
executables,
238+
path,
239+
version: ''
240+
});
25241
}
26242
}
27243

@@ -45,6 +261,17 @@ export async function detect(options: {
45261
});
46262
}
47263

264+
function findExecutables<T extends Record<string, string>>(dir: string, exes: T): Record<keyof T, string> {
265+
const executables: Record<string, string> = {};
266+
for (const [ name, exe ] of Object.entries(exes)) {
267+
const p = join(dir, exe);
268+
if (isFile(p)) {
269+
executables[name] = p;
270+
}
271+
}
272+
return executables as Record<keyof T, string>;
273+
}
274+
48275
async function getSearchPaths(options: { searchPaths?: string[] }) {
49276
const paths: string[] = [];
50277
if (Array.isArray(options.searchPaths)) {

0 commit comments

Comments
 (0)