andrud 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +295 -306
- package/dist/__tests__/context.test.d.ts +5 -0
- package/dist/__tests__/context.test.d.ts.map +1 -0
- package/dist/__tests__/context.test.js +86 -0
- package/dist/__tests__/context.test.js.map +1 -0
- package/dist/__tests__/generator.test.d.ts +5 -0
- package/dist/__tests__/generator.test.d.ts.map +1 -0
- package/dist/__tests__/generator.test.js +83 -0
- package/dist/__tests__/generator.test.js.map +1 -0
- package/dist/__tests__/validation.test.d.ts +5 -0
- package/dist/__tests__/validation.test.d.ts.map +1 -0
- package/dist/__tests__/validation.test.js +81 -0
- package/dist/__tests__/validation.test.js.map +1 -0
- package/dist/cli/commands/create.d.ts +10 -0
- package/dist/cli/commands/create.d.ts.map +1 -0
- package/dist/cli/commands/create.js +203 -0
- package/dist/cli/commands/create.js.map +1 -0
- package/dist/cli/commands/info.d.ts +13 -0
- package/dist/cli/commands/info.d.ts.map +1 -0
- package/dist/cli/commands/info.js +153 -0
- package/dist/cli/commands/info.js.map +1 -0
- package/dist/cli/commands/init.d.ts +15 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +141 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/list.d.ts +18 -0
- package/dist/cli/commands/list.d.ts.map +1 -0
- package/dist/cli/commands/list.js +122 -0
- package/dist/cli/commands/list.js.map +1 -0
- package/dist/cli/commands/new.d.ts +22 -0
- package/dist/cli/commands/new.d.ts.map +1 -0
- package/dist/cli/commands/new.js +245 -0
- package/dist/cli/commands/new.js.map +1 -0
- package/dist/cli/index.d.ts +5 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +157 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/core/config.d.ts +89 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +151 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/context.d.ts +47 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +175 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/generator.d.ts +44 -0
- package/dist/core/generator.d.ts.map +1 -0
- package/{src/core/generator.ts → dist/core/generator.js} +394 -484
- package/dist/core/generator.js.map +1 -0
- package/dist/core/types.d.ts +125 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +22 -0
- package/dist/core/types.js.map +1 -0
- package/dist/templates/index.d.ts +36 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/templates/index.js +141 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/ui/colors.d.ts +40 -0
- package/dist/ui/colors.d.ts.map +1 -0
- package/dist/ui/colors.js +117 -0
- package/dist/ui/colors.js.map +1 -0
- package/dist/ui/output.d.ts +69 -0
- package/dist/ui/output.d.ts.map +1 -0
- package/dist/ui/output.js +199 -0
- package/dist/ui/output.js.map +1 -0
- package/dist/ui/prompts.d.ts +20 -0
- package/dist/ui/prompts.d.ts.map +1 -0
- package/dist/ui/prompts.js +118 -0
- package/dist/ui/prompts.js.map +1 -0
- package/dist/ui/spinners.d.ts +30 -0
- package/dist/ui/spinners.d.ts.map +1 -0
- package/dist/ui/spinners.js +74 -0
- package/dist/ui/spinners.js.map +1 -0
- package/dist/ui/types.d.ts +35 -0
- package/dist/ui/types.d.ts.map +1 -0
- package/dist/ui/types.js +5 -0
- package/dist/ui/types.js.map +1 -0
- package/dist/utils/filesystem.d.ts +38 -0
- package/dist/utils/filesystem.d.ts.map +1 -0
- package/dist/utils/filesystem.js +181 -0
- package/dist/utils/filesystem.js.map +1 -0
- package/dist/utils/logger.d.ts +27 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +52 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/object.d.ts +140 -0
- package/dist/utils/object.d.ts.map +1 -0
- package/dist/utils/object.js +385 -0
- package/dist/utils/object.js.map +1 -0
- package/dist/utils/validation.d.ts +35 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +270 -0
- package/dist/utils/validation.js.map +1 -0
- package/package.json +10 -21
- package/CHANGELOG.md +0 -70
- package/CONTRIBUTING.md +0 -132
- package/sc.png +0 -0
- package/src/__tests__/context.test.ts +0 -133
- package/src/__tests__/generator.test.ts +0 -107
- package/src/__tests__/validation.test.ts +0 -105
- package/src/cli/commands/create.ts +0 -252
- package/src/cli/commands/info.ts +0 -178
- package/src/cli/commands/init.ts +0 -186
- package/src/cli/commands/list.ts +0 -156
- package/src/cli/commands/new.ts +0 -316
- package/src/cli/index.ts +0 -116
- package/src/core/config.ts +0 -172
- package/src/core/context.ts +0 -212
- package/src/core/types.ts +0 -184
- package/src/templates/index.ts +0 -162
- package/src/types/gradient-string.d.ts +0 -25
- package/src/ui/colors.ts +0 -139
- package/src/ui/output.ts +0 -230
- package/src/ui/prompts.ts +0 -170
- package/src/ui/spinners.ts +0 -95
- package/src/ui/types.ts +0 -41
- package/src/utils/filesystem.ts +0 -222
- package/src/utils/logger.ts +0 -67
- package/src/utils/object.ts +0 -456
- package/src/utils/validation.ts +0 -345
- package/tsconfig.json +0 -25
|
@@ -1,242 +1,204 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Project generator - creates Android project structure and files
|
|
3
3
|
*/
|
|
4
|
-
|
|
5
4
|
import { exists, writeFile, createDirectory, isDirectory } from '../utils/filesystem.js';
|
|
6
|
-
import type { TemplateContext, GenerationResult, GeneratedFile } from './types.js';
|
|
7
|
-
import { buildTemplateContext, buildDefaultProjectContext } from './context.js';
|
|
8
5
|
import { GRADLE_VERSIONS } from './config.js';
|
|
9
6
|
import pc from 'picocolors';
|
|
10
7
|
import { join } from 'path';
|
|
11
|
-
|
|
12
|
-
export interface GeneratorOptions {
|
|
13
|
-
overwrite?: boolean;
|
|
14
|
-
skipInstall?: boolean;
|
|
15
|
-
dryRun?: boolean;
|
|
16
|
-
verbose?: boolean;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
8
|
/**
|
|
20
9
|
* Validate project directory can be used
|
|
21
10
|
*/
|
|
22
|
-
export async function validateProjectDirectory(
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
valid: false,
|
|
38
|
-
error: 'Directory is not empty. Use --force to overwrite existing files.',
|
|
39
|
-
existingFiles: dirContent
|
|
40
|
-
};
|
|
11
|
+
export async function validateProjectDirectory(projectPath, options = { overwrite: false }) {
|
|
12
|
+
const pathExists = await exists(projectPath);
|
|
13
|
+
if (pathExists) {
|
|
14
|
+
const isDir = await isDirectory(projectPath);
|
|
15
|
+
if (!isDir) {
|
|
16
|
+
return { valid: false, error: 'Project path exists but is not a directory' };
|
|
17
|
+
}
|
|
18
|
+
const dirContent = await getDirectoryContents(projectPath);
|
|
19
|
+
if (dirContent.length > 0 && !options.overwrite) {
|
|
20
|
+
return {
|
|
21
|
+
valid: false,
|
|
22
|
+
error: 'Directory is not empty. Use --force to overwrite existing files.',
|
|
23
|
+
existingFiles: dirContent
|
|
24
|
+
};
|
|
25
|
+
}
|
|
41
26
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return { valid: true };
|
|
27
|
+
return { valid: true };
|
|
45
28
|
}
|
|
46
|
-
|
|
47
29
|
/**
|
|
48
30
|
* Validate context has all required fields
|
|
49
31
|
*/
|
|
50
|
-
export function validateContext(context
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
32
|
+
export function validateContext(context) {
|
|
33
|
+
const errors = [];
|
|
34
|
+
if (!context.appName)
|
|
35
|
+
errors.push('appName is required');
|
|
36
|
+
if (!context.packageName)
|
|
37
|
+
errors.push('packageName is required');
|
|
38
|
+
if (!context.projectDirectory)
|
|
39
|
+
errors.push('projectDirectory is required');
|
|
40
|
+
if (!context.template)
|
|
41
|
+
errors.push('template is required');
|
|
42
|
+
if (!context.language)
|
|
43
|
+
errors.push('language is required');
|
|
44
|
+
if (!context.uiFramework)
|
|
45
|
+
errors.push('uiFramework is required');
|
|
46
|
+
return { valid: errors.length === 0, errors };
|
|
61
47
|
}
|
|
62
|
-
|
|
63
48
|
/**
|
|
64
49
|
* Prepare project directory structure
|
|
65
50
|
*/
|
|
66
|
-
export async function prepareProjectStructure(
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
'
|
|
89
|
-
'
|
|
90
|
-
|
|
91
|
-
context.
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const packagePath = context.packageName.replace(/\./g, '/');
|
|
95
|
-
directories.push(`app/src/main/java/${packagePath}`);
|
|
96
|
-
|
|
97
|
-
if (context.language === 'kotlin') {
|
|
98
|
-
directories.push(`app/src/main/kotlin/${packagePath}`);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Add UI theme directories for Jetpack Compose projects
|
|
102
|
-
if (context.uiFramework === 'compose' && context.language === 'kotlin') {
|
|
103
|
-
directories.push(`app/src/main/kotlin/${packagePath}/ui`);
|
|
104
|
-
directories.push(`app/src/main/kotlin/${packagePath}/ui/theme`);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Add native cpp directories
|
|
108
|
-
if (context.template === 'native-cpp') {
|
|
109
|
-
directories.push(`app/src/main/cpp`);
|
|
110
|
-
directories.push(`app/src/main/jni`);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
for (const dir of directories) {
|
|
114
|
-
const fullPath = join(projectPath, dir);
|
|
115
|
-
try {
|
|
116
|
-
await createDirectory(fullPath);
|
|
117
|
-
createdPaths.push(dir);
|
|
118
|
-
} catch (error) {
|
|
119
|
-
errors.push(`Failed to create directory ${dir}: ${(error as Error).message}`);
|
|
51
|
+
export async function prepareProjectStructure(projectPath, context) {
|
|
52
|
+
const createdPaths = [];
|
|
53
|
+
const errors = [];
|
|
54
|
+
const directories = [
|
|
55
|
+
'',
|
|
56
|
+
'gradle/wrapper',
|
|
57
|
+
'app/src/main/java',
|
|
58
|
+
'app/src/main/res/layout',
|
|
59
|
+
'app/src/main/res/values',
|
|
60
|
+
'app/src/main/res/values-night',
|
|
61
|
+
'app/src/main/res/drawable',
|
|
62
|
+
'app/src/main/res/xml',
|
|
63
|
+
'app/src/main/res/mipmap-anydpi-v26',
|
|
64
|
+
'app/src/main/res/mipmap-hdpi',
|
|
65
|
+
'app/src/main/res/mipmap-mdpi',
|
|
66
|
+
'app/src/main/res/mipmap-xhdpi',
|
|
67
|
+
'app/src/main/res/mipmap-xxhdpi',
|
|
68
|
+
'app/src/main/res/mipmap-xxxhdpi',
|
|
69
|
+
'app/src/test/java',
|
|
70
|
+
'app/src/androidTest/java',
|
|
71
|
+
context.language === 'kotlin' ? 'app/src/main/kotlin' : '',
|
|
72
|
+
context.template === 'native-cpp' ? 'app/src/main/cpp' : ''
|
|
73
|
+
].filter(d => d !== '');
|
|
74
|
+
const packagePath = context.packageName.replace(/\./g, '/');
|
|
75
|
+
directories.push(`app/src/main/java/${packagePath}`);
|
|
76
|
+
if (context.language === 'kotlin') {
|
|
77
|
+
directories.push(`app/src/main/kotlin/${packagePath}`);
|
|
120
78
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
79
|
+
// Add UI theme directories for Jetpack Compose projects
|
|
80
|
+
if (context.uiFramework === 'compose' && context.language === 'kotlin') {
|
|
81
|
+
directories.push(`app/src/main/kotlin/${packagePath}/ui`);
|
|
82
|
+
directories.push(`app/src/main/kotlin/${packagePath}/ui/theme`);
|
|
83
|
+
}
|
|
84
|
+
// Add native cpp directories
|
|
85
|
+
if (context.template === 'native-cpp') {
|
|
86
|
+
directories.push(`app/src/main/cpp`);
|
|
87
|
+
directories.push(`app/src/main/jni`);
|
|
88
|
+
}
|
|
89
|
+
for (const dir of directories) {
|
|
90
|
+
const fullPath = join(projectPath, dir);
|
|
91
|
+
try {
|
|
92
|
+
await createDirectory(fullPath);
|
|
93
|
+
createdPaths.push(dir);
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
errors.push(`Failed to create directory ${dir}: ${error.message}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return { success: errors.length === 0, createdPaths, errors };
|
|
124
100
|
}
|
|
125
|
-
|
|
126
101
|
/**
|
|
127
102
|
* Generate all project files
|
|
128
103
|
*/
|
|
129
|
-
export async function generateProject(
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
104
|
+
export async function generateProject(context, options = {}) {
|
|
105
|
+
const startTime = Date.now();
|
|
106
|
+
const generatedFiles = [];
|
|
107
|
+
const skippedFiles = [];
|
|
108
|
+
const errors = [];
|
|
109
|
+
const warnings = [];
|
|
110
|
+
const projectPath = context.projectDirectory;
|
|
111
|
+
const contextValidation = validateContext(context);
|
|
112
|
+
if (!contextValidation.valid) {
|
|
113
|
+
return {
|
|
114
|
+
success: false,
|
|
115
|
+
projectPath,
|
|
116
|
+
generatedFiles: [],
|
|
117
|
+
skippedFiles: [],
|
|
118
|
+
errors: contextValidation.errors.map(msg => ({ message: msg })),
|
|
119
|
+
warnings,
|
|
120
|
+
duration: Date.now() - startTime
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
const dirValidation = await validateProjectDirectory(projectPath, { overwrite: options.overwrite ?? false });
|
|
124
|
+
if (!dirValidation.valid) {
|
|
125
|
+
return {
|
|
126
|
+
success: false,
|
|
127
|
+
projectPath,
|
|
128
|
+
generatedFiles: [],
|
|
129
|
+
skippedFiles: [],
|
|
130
|
+
errors: [{ message: dirValidation.error, code: 'DIR_VALIDATION_ERROR' }],
|
|
131
|
+
warnings,
|
|
132
|
+
duration: Date.now() - startTime
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
const prepResult = await prepareProjectStructure(projectPath, context);
|
|
136
|
+
if (!prepResult.success) {
|
|
137
|
+
errors.push(...prepResult.errors.map(msg => ({ message: msg, code: 'DIR_CREATE_ERROR' })));
|
|
138
|
+
}
|
|
139
|
+
generatedFiles.push(...prepResult.createdPaths);
|
|
140
|
+
const filesToGenerate = [
|
|
141
|
+
generateSettingsGradle(context),
|
|
142
|
+
generateRootBuildGradle(context),
|
|
143
|
+
generateGradleProperties(context),
|
|
144
|
+
generateGitIgnore(context),
|
|
145
|
+
generateReadme(context),
|
|
146
|
+
generateGradleWrapperProperties(context),
|
|
147
|
+
generateGradlewBat(context),
|
|
148
|
+
generateGradlewUnix(context),
|
|
149
|
+
generateAppBuildGradle(context),
|
|
150
|
+
generateAppProguardRules(context),
|
|
151
|
+
generateAppManifest(context),
|
|
152
|
+
generateApplicationClass(context),
|
|
153
|
+
generateMainActivity(context),
|
|
154
|
+
generateStrings(context),
|
|
155
|
+
generateColors(context),
|
|
156
|
+
generateThemes(context),
|
|
157
|
+
generateAppIcon(context),
|
|
158
|
+
generateActivityLayout(context),
|
|
159
|
+
...generateSourceSetFiles(context)
|
|
160
|
+
].filter((f) => f !== null && typeof f === 'object' && 'path' in f);
|
|
161
|
+
for (const file of filesToGenerate) {
|
|
162
|
+
try {
|
|
163
|
+
const filePath = join(projectPath, file.path);
|
|
164
|
+
const fileExists = await exists(filePath);
|
|
165
|
+
if (fileExists && !options.overwrite && file.overwrite !== true) {
|
|
166
|
+
skippedFiles.push(file.path);
|
|
167
|
+
if (options.verbose) {
|
|
168
|
+
console.log(pc.yellow(`Skipped: ${file.path} (already exists)`));
|
|
169
|
+
}
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
await writeFile(filePath, file.content);
|
|
173
|
+
generatedFiles.push(file.path);
|
|
174
|
+
if (options.verbose) {
|
|
175
|
+
console.log(pc.green(`Generated: ${file.path}`));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
errors.push({
|
|
180
|
+
file: file.path,
|
|
181
|
+
message: error.message,
|
|
182
|
+
code: 'WRITE_ERROR'
|
|
183
|
+
});
|
|
203
184
|
}
|
|
204
|
-
continue;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
await writeFile(filePath, file.content);
|
|
208
|
-
generatedFiles.push(file.path);
|
|
209
|
-
|
|
210
|
-
if (options.verbose) {
|
|
211
|
-
console.log(pc.green(`Generated: ${file.path}`));
|
|
212
|
-
}
|
|
213
|
-
} catch (error) {
|
|
214
|
-
errors.push({
|
|
215
|
-
file: file.path,
|
|
216
|
-
message: (error as Error).message,
|
|
217
|
-
code: 'WRITE_ERROR'
|
|
218
|
-
});
|
|
219
185
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
warnings,
|
|
232
|
-
duration
|
|
233
|
-
};
|
|
186
|
+
const duration = Date.now() - startTime;
|
|
187
|
+
const success = errors.length === 0;
|
|
188
|
+
return {
|
|
189
|
+
success,
|
|
190
|
+
projectPath,
|
|
191
|
+
generatedFiles,
|
|
192
|
+
skippedFiles,
|
|
193
|
+
errors,
|
|
194
|
+
warnings,
|
|
195
|
+
duration
|
|
196
|
+
};
|
|
234
197
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
content: `pluginManagement {
|
|
198
|
+
function generateSettingsGradle(ctx) {
|
|
199
|
+
return {
|
|
200
|
+
path: 'settings.gradle.kts',
|
|
201
|
+
content: `pluginManagement {
|
|
240
202
|
repositories {
|
|
241
203
|
google()
|
|
242
204
|
mavenCentral()
|
|
@@ -255,32 +217,27 @@ dependencyResolutionManagement {
|
|
|
255
217
|
rootProject.name = "${ctx.appName}"
|
|
256
218
|
include(":app")
|
|
257
219
|
`
|
|
258
|
-
|
|
220
|
+
};
|
|
259
221
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
const kotlinPlugin = ctx.language === 'kotlin' ? `
|
|
222
|
+
function generateRootBuildGradle(ctx) {
|
|
223
|
+
const kotlinPlugin = ctx.language === 'kotlin' ? `
|
|
263
224
|
id("org.jetbrains.kotlin.android") version "${ctx.gradle.kotlinVersion || GRADLE_VERSIONS.KOTLIN}" apply false` : '';
|
|
264
|
-
|
|
265
|
-
const composePlugin = ctx.uiFramework === 'compose' ? `
|
|
225
|
+
const composePlugin = ctx.uiFramework === 'compose' ? `
|
|
266
226
|
id("org.jetbrains.kotlin.plugin.compose") version "${ctx.gradle.kotlinVersion || GRADLE_VERSIONS.KOTLIN}" apply false` : '';
|
|
267
|
-
|
|
268
|
-
const kaptPlugin = ctx.language === 'kotlin' ? `
|
|
227
|
+
const kaptPlugin = ctx.language === 'kotlin' ? `
|
|
269
228
|
id("org.jetbrains.kotlin.kapt") version "${ctx.gradle.kotlinVersion || GRADLE_VERSIONS.KOTLIN}" apply false` : '';
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
content: `plugins {
|
|
229
|
+
return {
|
|
230
|
+
path: 'build.gradle.kts',
|
|
231
|
+
content: `plugins {
|
|
274
232
|
id("com.android.application") version "${ctx.gradle.agpVersion}" apply false${kotlinPlugin}${composePlugin}${kaptPlugin}
|
|
275
233
|
}
|
|
276
234
|
`
|
|
277
|
-
|
|
235
|
+
};
|
|
278
236
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
content: `# Project-wide Gradle settings
|
|
237
|
+
function generateGradleProperties(_ctx) {
|
|
238
|
+
return {
|
|
239
|
+
path: 'gradle.properties',
|
|
240
|
+
content: `# Project-wide Gradle settings
|
|
284
241
|
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
|
285
242
|
org.gradle.parallel=true
|
|
286
243
|
org.gradle.caching=true
|
|
@@ -294,13 +251,12 @@ android.suppressUnsupportedCompileSdk=36
|
|
|
294
251
|
# Kotlin settings
|
|
295
252
|
kotlin.code.style=official
|
|
296
253
|
`
|
|
297
|
-
|
|
254
|
+
};
|
|
298
255
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
content: `# Built application files
|
|
256
|
+
function generateGitIgnore(_ctx) {
|
|
257
|
+
return {
|
|
258
|
+
path: '.gitignore',
|
|
259
|
+
content: `# Built application files
|
|
304
260
|
*.apk
|
|
305
261
|
*.ap_
|
|
306
262
|
*.aab
|
|
@@ -325,16 +281,14 @@ captures/
|
|
|
325
281
|
google-services.json
|
|
326
282
|
vcs.xml
|
|
327
283
|
`
|
|
328
|
-
|
|
284
|
+
};
|
|
329
285
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
path: 'README.md',
|
|
337
|
-
content: `# ${ctx.appName}
|
|
286
|
+
function generateReadme(ctx) {
|
|
287
|
+
const lang = ctx.language === 'kotlin' ? 'Kotlin' : 'Java';
|
|
288
|
+
const ui = ctx.uiFramework === 'compose' ? 'Jetpack Compose' : 'XML Layouts';
|
|
289
|
+
return {
|
|
290
|
+
path: 'README.md',
|
|
291
|
+
content: `# ${ctx.appName}
|
|
338
292
|
|
|
339
293
|
An Android application built with ${lang} using ${ui}.
|
|
340
294
|
|
|
@@ -352,13 +306,12 @@ An Android application built with ${lang} using ${ui}.
|
|
|
352
306
|
## License
|
|
353
307
|
MIT License
|
|
354
308
|
`
|
|
355
|
-
|
|
309
|
+
};
|
|
356
310
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
content: `distributionBase=GRADLE_USER_HOME
|
|
311
|
+
function generateGradleWrapperProperties(ctx) {
|
|
312
|
+
return {
|
|
313
|
+
path: 'gradle/wrapper/gradle-wrapper.properties',
|
|
314
|
+
content: `distributionBase=GRADLE_USER_HOME
|
|
362
315
|
distributionPath=wrapper/dists
|
|
363
316
|
distributionUrl=https\\://services.gradle.org/distributions/gradle-${ctx.gradle.gradleVersion}-bin.zip
|
|
364
317
|
networkTimeout=10000
|
|
@@ -366,13 +319,12 @@ validateDistributionUrl=true
|
|
|
366
319
|
zipStoreBase=GRADLE_USER_HOME
|
|
367
320
|
zipStorePath=wrapper/dists
|
|
368
321
|
`
|
|
369
|
-
|
|
322
|
+
};
|
|
370
323
|
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
content: `@echo off
|
|
324
|
+
function generateGradlewBat(_ctx) {
|
|
325
|
+
return {
|
|
326
|
+
path: 'gradlew.bat',
|
|
327
|
+
content: `@echo off
|
|
376
328
|
setlocal enabledelayedexpansion
|
|
377
329
|
set DIRNAME=%~dp0
|
|
378
330
|
if "%DIRNAME%"=="" set DIRNAME=.
|
|
@@ -395,13 +347,12 @@ exit /b 1
|
|
|
395
347
|
:mainEnd
|
|
396
348
|
endlocal
|
|
397
349
|
`
|
|
398
|
-
|
|
350
|
+
};
|
|
399
351
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
content: `#!/bin/sh
|
|
352
|
+
function generateGradlewUnix(_ctx) {
|
|
353
|
+
return {
|
|
354
|
+
path: 'gradlew',
|
|
355
|
+
content: `#!/bin/sh
|
|
405
356
|
|
|
406
357
|
APP_HOME=$(dirname "$(cd "$(dirname "$0")" && pwd)")
|
|
407
358
|
CLASSPATH="$APP_HOME/gradle/wrapper/gradle-wrapper.jar"
|
|
@@ -421,23 +372,20 @@ DEFAULT_JVM_OPTS="-Xmx64m -Xms64m"
|
|
|
421
372
|
|
|
422
373
|
exec "$JAVACMD" $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "-Dorg.gradle.appname=$(basename "$0")" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
|
423
374
|
`
|
|
424
|
-
|
|
375
|
+
};
|
|
425
376
|
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
plugins += ' id("org.jetbrains.kotlin.plugin.compose")\n';
|
|
377
|
+
function generateAppBuildGradle(ctx) {
|
|
378
|
+
const isCompose = ctx.uiFramework === 'compose';
|
|
379
|
+
const isKotlin = ctx.language === 'kotlin';
|
|
380
|
+
let plugins = 'plugins {\n id("com.android.application")\n';
|
|
381
|
+
if (isKotlin) {
|
|
382
|
+
plugins += ' id("org.jetbrains.kotlin.android")\n';
|
|
383
|
+
if (isCompose) {
|
|
384
|
+
plugins += ' id("org.jetbrains.kotlin.plugin.compose")\n';
|
|
385
|
+
}
|
|
436
386
|
}
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
let config = `android {
|
|
387
|
+
plugins += '}\n';
|
|
388
|
+
let config = `android {
|
|
441
389
|
namespace = "${ctx.packageName}"
|
|
442
390
|
compileSdk = ${ctx.android.compileSdk}
|
|
443
391
|
|
|
@@ -449,14 +397,13 @@ function generateAppBuildGradle(ctx: TemplateContext): GeneratedFile {
|
|
|
449
397
|
versionName = "1.0.0"
|
|
450
398
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
|
451
399
|
`;
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
config += ` vectorDrawables {
|
|
400
|
+
if (isCompose) {
|
|
401
|
+
config += ` vectorDrawables {
|
|
455
402
|
useSupportLibrary = true
|
|
456
403
|
}
|
|
457
404
|
`;
|
|
458
|
-
|
|
459
|
-
|
|
405
|
+
}
|
|
406
|
+
config += ` }
|
|
460
407
|
|
|
461
408
|
buildTypes {
|
|
462
409
|
release {
|
|
@@ -473,22 +420,19 @@ function generateAppBuildGradle(ctx: TemplateContext): GeneratedFile {
|
|
|
473
420
|
targetCompatibility = JavaVersion.VERSION_17
|
|
474
421
|
}
|
|
475
422
|
`;
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
config += ` kotlinOptions {
|
|
423
|
+
if (isKotlin) {
|
|
424
|
+
config += ` kotlinOptions {
|
|
479
425
|
jvmTarget = "17"
|
|
480
426
|
}
|
|
481
427
|
`;
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
config += ` buildFeatures {
|
|
428
|
+
}
|
|
429
|
+
if (isCompose) {
|
|
430
|
+
config += ` buildFeatures {
|
|
486
431
|
compose = true
|
|
487
432
|
}
|
|
488
433
|
`;
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
config += ` packaging {
|
|
434
|
+
}
|
|
435
|
+
config += ` packaging {
|
|
492
436
|
resources {
|
|
493
437
|
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
|
494
438
|
}
|
|
@@ -501,9 +445,8 @@ dependencies {
|
|
|
501
445
|
implementation("com.google.android.material:material:1.12.0")
|
|
502
446
|
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
|
503
447
|
`;
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
config += ` implementation(platform("androidx.compose:compose-bom:2025.01.00"))
|
|
448
|
+
if (isCompose) {
|
|
449
|
+
config += ` implementation(platform("androidx.compose:compose-bom:2025.01.00"))
|
|
507
450
|
implementation("androidx.compose.ui:ui")
|
|
508
451
|
implementation("androidx.compose.ui:ui-graphics")
|
|
509
452
|
implementation("androidx.compose.ui:ui-tooling-preview")
|
|
@@ -515,21 +458,18 @@ dependencies {
|
|
|
515
458
|
debugImplementation("androidx.compose.ui:ui-tooling")
|
|
516
459
|
debugImplementation("androidx.compose.ui:ui-test-manifest")
|
|
517
460
|
`;
|
|
518
|
-
|
|
519
|
-
|
|
461
|
+
}
|
|
462
|
+
else {
|
|
463
|
+
config += ` implementation("androidx.activity:activity-ktx:1.9.2")
|
|
520
464
|
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.5")
|
|
521
465
|
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.5")
|
|
522
466
|
`;
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
config = config.replace(
|
|
531
|
-
'kotlinOptions {',
|
|
532
|
-
`externalNativeBuild {
|
|
467
|
+
}
|
|
468
|
+
// Add native C++ configuration if needed
|
|
469
|
+
if (ctx.template === 'native-cpp') {
|
|
470
|
+
const ndkVersion = ctx.nativeCpp?.ndkVersion ?? '28.2.13676358';
|
|
471
|
+
const abiFilters = ctx.nativeCpp?.abiFilters?.join(', ') ?? '"armeabi-v7a", "arm64-v8a", "x86", "x86_64"';
|
|
472
|
+
config = config.replace('kotlinOptions {', `externalNativeBuild {
|
|
533
473
|
cmake {
|
|
534
474
|
cppFlags += "-std=c++17"
|
|
535
475
|
arguments += "-DANDROID_STL=c++_static"
|
|
@@ -537,29 +477,25 @@ dependencies {
|
|
|
537
477
|
}
|
|
538
478
|
|
|
539
479
|
ndk {
|
|
540
|
-
abiFilters += [${abiFilters.split(', ').map((a
|
|
480
|
+
abiFilters += [${abiFilters.split(', ').map((a) => `"${a.replace(/"/g, '')}"`).join(', ')}]
|
|
541
481
|
}
|
|
542
482
|
|
|
543
|
-
kotlinOptions {`
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
config += ` testImplementation("junit:junit:4.13.2")
|
|
483
|
+
kotlinOptions {`);
|
|
484
|
+
}
|
|
485
|
+
config += ` testImplementation("junit:junit:4.13.2")
|
|
548
486
|
androidTestImplementation("androidx.test.ext:junit:1.2.1")
|
|
549
487
|
androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")
|
|
550
488
|
}
|
|
551
489
|
`;
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
};
|
|
490
|
+
return {
|
|
491
|
+
path: 'app/build.gradle.kts',
|
|
492
|
+
content: plugins + config
|
|
493
|
+
};
|
|
557
494
|
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
content: `# ProGuard rules for Android
|
|
495
|
+
function generateAppProguardRules(_ctx) {
|
|
496
|
+
return {
|
|
497
|
+
path: 'app/proguard-rules.pro',
|
|
498
|
+
content: `# ProGuard rules for Android
|
|
563
499
|
-keepattributes SourceFile,LineNumberTable
|
|
564
500
|
-renamesourcefileattribute SourceFile
|
|
565
501
|
-keep class kotlin.** { *; }
|
|
@@ -568,15 +504,13 @@ function generateAppProguardRules(_ctx: TemplateContext): GeneratedFile {
|
|
|
568
504
|
-keep class androidx.** { *; }
|
|
569
505
|
-keep interface androidx.** { *; }
|
|
570
506
|
`
|
|
571
|
-
|
|
507
|
+
};
|
|
572
508
|
}
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
path: 'app/src/main/AndroidManifest.xml',
|
|
579
|
-
content: `<?xml version="1.0" encoding="utf-8"?>
|
|
509
|
+
function generateAppManifest(ctx) {
|
|
510
|
+
const activityClass = ctx.language === 'kotlin' ? 'MainActivity' : '.MainActivity';
|
|
511
|
+
return {
|
|
512
|
+
path: 'app/src/main/AndroidManifest.xml',
|
|
513
|
+
content: `<?xml version="1.0" encoding="utf-8"?>
|
|
580
514
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
581
515
|
|
|
582
516
|
<application
|
|
@@ -601,17 +535,15 @@ function generateAppManifest(ctx: TemplateContext): GeneratedFile {
|
|
|
601
535
|
|
|
602
536
|
</manifest>
|
|
603
537
|
`
|
|
604
|
-
|
|
538
|
+
};
|
|
605
539
|
}
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
path: `app/src/main/kotlin/${packagePath}/${className}.kt`,
|
|
614
|
-
content: `package ${ctx.packageName}
|
|
540
|
+
function generateApplicationClass(ctx) {
|
|
541
|
+
const packagePath = ctx.packagePath;
|
|
542
|
+
const className = `${ctx.appNamePascal}Application`;
|
|
543
|
+
if (ctx.language === 'kotlin') {
|
|
544
|
+
return {
|
|
545
|
+
path: `app/src/main/kotlin/${packagePath}/${className}.kt`,
|
|
546
|
+
content: `package ${ctx.packageName}
|
|
615
547
|
|
|
616
548
|
import android.app.Application
|
|
617
549
|
|
|
@@ -621,11 +553,12 @@ class ${className} : Application() {
|
|
|
621
553
|
}
|
|
622
554
|
}
|
|
623
555
|
`
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
else {
|
|
559
|
+
return {
|
|
560
|
+
path: `app/src/main/java/${packagePath}/${className}.java`,
|
|
561
|
+
content: `package ${ctx.packageName};
|
|
629
562
|
|
|
630
563
|
import android.app.Application;
|
|
631
564
|
|
|
@@ -636,17 +569,15 @@ public class ${className} extends Application {
|
|
|
636
569
|
}
|
|
637
570
|
}
|
|
638
571
|
`
|
|
639
|
-
|
|
640
|
-
|
|
572
|
+
};
|
|
573
|
+
}
|
|
641
574
|
}
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
path: `app/src/main/kotlin/${packagePath}/MainActivity.kt`,
|
|
649
|
-
content: `package ${ctx.packageName}
|
|
575
|
+
function generateMainActivity(ctx) {
|
|
576
|
+
const packagePath = ctx.packagePath;
|
|
577
|
+
if (ctx.uiFramework === 'compose' && ctx.language === 'kotlin') {
|
|
578
|
+
return {
|
|
579
|
+
path: `app/src/main/kotlin/${packagePath}/MainActivity.kt`,
|
|
580
|
+
content: `package ${ctx.packageName}
|
|
650
581
|
|
|
651
582
|
import android.os.Bundle
|
|
652
583
|
import androidx.activity.ComponentActivity
|
|
@@ -752,13 +683,12 @@ fun MainScreen() {
|
|
|
752
683
|
}
|
|
753
684
|
}
|
|
754
685
|
`
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
content: `package ${ctx.packageName}
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
if (ctx.language === 'kotlin') {
|
|
689
|
+
return {
|
|
690
|
+
path: `app/src/main/kotlin/${packagePath}/MainActivity.kt`,
|
|
691
|
+
content: `package ${ctx.packageName}
|
|
762
692
|
|
|
763
693
|
import android.os.Bundle
|
|
764
694
|
import androidx.appcompat.app.AppCompatActivity
|
|
@@ -789,12 +719,11 @@ class MainActivity : AppCompatActivity() {
|
|
|
789
719
|
}
|
|
790
720
|
}
|
|
791
721
|
`
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
content: `package ${ctx.packageName};
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
return {
|
|
725
|
+
path: `app/src/main/java/${packagePath}/MainActivity.java`,
|
|
726
|
+
content: `package ${ctx.packageName};
|
|
798
727
|
|
|
799
728
|
import android.os.Bundle;
|
|
800
729
|
import androidx.appcompat.app.AppCompatActivity;
|
|
@@ -823,28 +752,25 @@ public class MainActivity extends AppCompatActivity {
|
|
|
823
752
|
}
|
|
824
753
|
}
|
|
825
754
|
`
|
|
826
|
-
|
|
755
|
+
};
|
|
827
756
|
}
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
path: 'app/src/main/res/values/strings.xml',
|
|
834
|
-
content: `<?xml version="1.0" encoding="utf-8"?>
|
|
757
|
+
function generateStrings(ctx) {
|
|
758
|
+
const displayName = ctx.appName.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
|
|
759
|
+
return {
|
|
760
|
+
path: 'app/src/main/res/values/strings.xml',
|
|
761
|
+
content: `<?xml version="1.0" encoding="utf-8"?>
|
|
835
762
|
<resources>
|
|
836
763
|
<string name="app_name">${displayName}</string>
|
|
837
764
|
<string name="app_name_short">${ctx.appNamePascal}</string>
|
|
838
765
|
<string name="content_description_app_icon">Application icon</string>
|
|
839
766
|
</resources>
|
|
840
767
|
`
|
|
841
|
-
|
|
768
|
+
};
|
|
842
769
|
}
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
content: `<?xml version="1.0" encoding="utf-8"?>
|
|
770
|
+
function generateColors(_ctx) {
|
|
771
|
+
return {
|
|
772
|
+
path: 'app/src/main/res/values/colors.xml',
|
|
773
|
+
content: `<?xml version="1.0" encoding="utf-8"?>
|
|
848
774
|
<resources>
|
|
849
775
|
<!-- Primary Colors -->
|
|
850
776
|
<color name="primary">#6750A4</color>
|
|
@@ -875,16 +801,14 @@ function generateColors(_ctx: TemplateContext): GeneratedFile {
|
|
|
875
801
|
<color name="ic_launcher_background">#6750A4</color>
|
|
876
802
|
</resources>
|
|
877
803
|
`
|
|
878
|
-
|
|
804
|
+
};
|
|
879
805
|
}
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
path: 'app/src/main/res/values/themes.xml',
|
|
887
|
-
content: `<?xml version="1.0" encoding="utf-8"?>
|
|
806
|
+
function generateThemes(ctx) {
|
|
807
|
+
const themeName = `Theme.${ctx.appNamePascal}`;
|
|
808
|
+
if (ctx.uiFramework === 'compose') {
|
|
809
|
+
return {
|
|
810
|
+
path: 'app/src/main/res/values/themes.xml',
|
|
811
|
+
content: `<?xml version="1.0" encoding="utf-8"?>
|
|
888
812
|
<resources>
|
|
889
813
|
<style name="${themeName}" parent="Theme.Material3.Light.NoActionBar">
|
|
890
814
|
<item name="android:statusBarColor">@android:color/transparent</item>
|
|
@@ -894,12 +818,11 @@ function generateThemes(ctx: TemplateContext): GeneratedFile {
|
|
|
894
818
|
</style>
|
|
895
819
|
</resources>
|
|
896
820
|
`
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
content: `<?xml version="1.0" encoding="utf-8"?>
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
return {
|
|
824
|
+
path: 'app/src/main/res/values/themes.xml',
|
|
825
|
+
content: `<?xml version="1.0" encoding="utf-8"?>
|
|
903
826
|
<resources>
|
|
904
827
|
<style name="${themeName}" parent="Theme.Material3.Light.NoActionBar">
|
|
905
828
|
<item name="colorPrimary">@color/primary</item>
|
|
@@ -923,26 +846,24 @@ function generateThemes(ctx: TemplateContext): GeneratedFile {
|
|
|
923
846
|
</style>
|
|
924
847
|
</resources>
|
|
925
848
|
`
|
|
926
|
-
|
|
849
|
+
};
|
|
927
850
|
}
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
content: `<?xml version="1.0" encoding="utf-8"?>
|
|
851
|
+
function generateAppIcon(_ctx) {
|
|
852
|
+
return {
|
|
853
|
+
path: 'app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml',
|
|
854
|
+
content: `<?xml version="1.0" encoding="utf-8"?>
|
|
933
855
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
934
856
|
<background android:drawable="@drawable/ic_launcher_background"/>
|
|
935
857
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
|
936
858
|
</adaptive-icon>
|
|
937
859
|
`
|
|
938
|
-
|
|
860
|
+
};
|
|
939
861
|
}
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
content: `<?xml version="1.0" encoding="utf-8"?>
|
|
862
|
+
function generateActivityLayout(ctx) {
|
|
863
|
+
if (ctx.uiFramework === 'compose') {
|
|
864
|
+
return {
|
|
865
|
+
path: 'app/src/main/res/layout/activity_main.xml',
|
|
866
|
+
content: `<?xml version="1.0" encoding="utf-8"?>
|
|
946
867
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
947
868
|
android:layout_width="match_parent"
|
|
948
869
|
android:layout_height="match_parent"
|
|
@@ -959,12 +880,11 @@ function generateActivityLayout(ctx: TemplateContext): GeneratedFile {
|
|
|
959
880
|
|
|
960
881
|
</FrameLayout>
|
|
961
882
|
`
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
content: `<?xml version="1.0" encoding="utf-8"?>
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
return {
|
|
886
|
+
path: 'app/src/main/res/layout/activity_main.xml',
|
|
887
|
+
content: `<?xml version="1.0" encoding="utf-8"?>
|
|
968
888
|
<androidx.constraintlayout.widget.ConstraintLayout
|
|
969
889
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
970
890
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
@@ -999,17 +919,15 @@ function generateActivityLayout(ctx: TemplateContext): GeneratedFile {
|
|
|
999
919
|
|
|
1000
920
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
1001
921
|
`
|
|
1002
|
-
|
|
922
|
+
};
|
|
1003
923
|
}
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
path: 'app/src/main/res/drawable/ic_launcher_background.xml',
|
|
1012
|
-
content: `<?xml version="1.0" encoding="utf-8"?>
|
|
924
|
+
function generateSourceSetFiles(ctx) {
|
|
925
|
+
const files = [];
|
|
926
|
+
const packagePath = ctx.packagePath;
|
|
927
|
+
// Professional adaptive icon background
|
|
928
|
+
files.push({
|
|
929
|
+
path: 'app/src/main/res/drawable/ic_launcher_background.xml',
|
|
930
|
+
content: `<?xml version="1.0" encoding="utf-8"?>
|
|
1013
931
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
1014
932
|
android:width="108dp"
|
|
1015
933
|
android:height="108dp"
|
|
@@ -1020,12 +938,11 @@ function generateSourceSetFiles(ctx: TemplateContext): GeneratedFile[] {
|
|
|
1020
938
|
android:pathData="M0,0h108v108h-108z"/>
|
|
1021
939
|
</vector>
|
|
1022
940
|
`
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
content: `<?xml version="1.0" encoding="utf-8"?>
|
|
941
|
+
});
|
|
942
|
+
// Professional icon foreground - Android robot stylized "A"
|
|
943
|
+
files.push({
|
|
944
|
+
path: 'app/src/main/res/drawable/ic_launcher_foreground.xml',
|
|
945
|
+
content: `<?xml version="1.0" encoding="utf-8"?>
|
|
1029
946
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
1030
947
|
android:width="108dp"
|
|
1031
948
|
android:height="108dp"
|
|
@@ -1053,22 +970,20 @@ function generateSourceSetFiles(ctx: TemplateContext): GeneratedFile[] {
|
|
|
1053
970
|
</group>
|
|
1054
971
|
</vector>
|
|
1055
972
|
`
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
content: `<?xml version="1.0" encoding="utf-8"?>
|
|
973
|
+
});
|
|
974
|
+
files.push({
|
|
975
|
+
path: 'app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml',
|
|
976
|
+
content: `<?xml version="1.0" encoding="utf-8"?>
|
|
1061
977
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
1062
978
|
<background android:drawable="@drawable/ic_launcher_background"/>
|
|
1063
979
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
|
1064
980
|
</adaptive-icon>
|
|
1065
981
|
`
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
content: `<?xml version="1.0" encoding="utf-8"?>
|
|
982
|
+
});
|
|
983
|
+
// Add icon for FAB
|
|
984
|
+
files.push({
|
|
985
|
+
path: 'app/src/main/res/drawable/ic_add.xml',
|
|
986
|
+
content: `<?xml version="1.0" encoding="utf-8"?>
|
|
1072
987
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
1073
988
|
android:width="24dp"
|
|
1074
989
|
android:height="24dp"
|
|
@@ -1079,12 +994,11 @@ function generateSourceSetFiles(ctx: TemplateContext): GeneratedFile[] {
|
|
|
1079
994
|
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
|
|
1080
995
|
</vector>
|
|
1081
996
|
`
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
content: `package ${ctx.packageName}.ui.theme
|
|
997
|
+
});
|
|
998
|
+
if (ctx.uiFramework === 'compose') {
|
|
999
|
+
files.push({
|
|
1000
|
+
path: `app/src/main/kotlin/${packagePath}/ui/theme/Color.kt`,
|
|
1001
|
+
content: `package ${ctx.packageName}.ui.theme
|
|
1088
1002
|
|
|
1089
1003
|
import androidx.compose.ui.graphics.Color
|
|
1090
1004
|
|
|
@@ -1102,11 +1016,10 @@ val Pink80 = Color(0xFFEFB8C8)
|
|
|
1102
1016
|
val Teal40 = Color(0xFF006B5B)
|
|
1103
1017
|
val Teal80 = Color(0xFF8BD8CE)
|
|
1104
1018
|
`
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
content: `package ${ctx.packageName}.ui.theme
|
|
1019
|
+
});
|
|
1020
|
+
files.push({
|
|
1021
|
+
path: `app/src/main/kotlin/${packagePath}/ui/theme/Theme.kt`,
|
|
1022
|
+
content: `package ${ctx.packageName}.ui.theme
|
|
1110
1023
|
|
|
1111
1024
|
import android.app.Activity
|
|
1112
1025
|
import android.os.Build
|
|
@@ -1199,11 +1112,10 @@ fun ${ctx.appNamePascal}Theme(
|
|
|
1199
1112
|
)
|
|
1200
1113
|
}
|
|
1201
1114
|
`
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
content: `package ${ctx.packageName}.ui.theme
|
|
1115
|
+
});
|
|
1116
|
+
files.push({
|
|
1117
|
+
path: `app/src/main/kotlin/${packagePath}/ui/theme/Type.kt`,
|
|
1118
|
+
content: `package ${ctx.packageName}.ui.theme
|
|
1207
1119
|
|
|
1208
1120
|
import androidx.compose.material3.Typography
|
|
1209
1121
|
import androidx.compose.ui.text.TextStyle
|
|
@@ -1319,32 +1231,30 @@ val Typography = Typography(
|
|
|
1319
1231
|
)
|
|
1320
1232
|
)
|
|
1321
1233
|
`
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
return files;
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1236
|
+
return files;
|
|
1326
1237
|
}
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
return ctx.language === 'kotlin';
|
|
1238
|
+
function isKotlinActivity(ctx) {
|
|
1239
|
+
return ctx.language === 'kotlin';
|
|
1330
1240
|
}
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
return versions[apiLevel] || 'Unknown';
|
|
1241
|
+
function getAndroidVersionName(apiLevel) {
|
|
1242
|
+
const versions = {
|
|
1243
|
+
35: '15', 34: '14', 33: '13', 32: '12L', 31: '12',
|
|
1244
|
+
30: '11', 29: '10', 28: '9', 27: '8.1', 26: '8.0', 24: '7.0', 21: '5.0'
|
|
1245
|
+
};
|
|
1246
|
+
return versions[apiLevel] || 'Unknown';
|
|
1338
1247
|
}
|
|
1339
|
-
|
|
1340
1248
|
/**
|
|
1341
1249
|
* Get directory contents
|
|
1342
1250
|
*/
|
|
1343
|
-
export async function getDirectoryContents(path
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
}
|
|
1251
|
+
export async function getDirectoryContents(path) {
|
|
1252
|
+
try {
|
|
1253
|
+
const items = await import('fs-extra').then(fse => fse.default.readdir(path));
|
|
1254
|
+
return items;
|
|
1255
|
+
}
|
|
1256
|
+
catch (error) {
|
|
1257
|
+
return [];
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
//# sourceMappingURL=generator.js.map
|