1import assert from 'assert'; 2import fs from 'fs'; 3import { sync as globSync } from 'glob'; 4import * as path from 'path'; 5 6import { ResourceKind } from './Resources'; 7import { UnexpectedError } from '../utils/errors'; 8import { directoryExistsAsync } from '../utils/modules'; 9 10export interface ProjectFile<L extends string = string> { 11 path: string; 12 language: L; 13 contents: string; 14} 15 16export type ApplicationProjectFile = ProjectFile<'java' | 'kt'>; 17export type GradleProjectFile = ProjectFile<'groovy' | 'kt'>; 18 19export function getProjectFilePath(projectRoot: string, name: string): string { 20 const filePath = globSync( 21 path.join(projectRoot, `android/app/src/main/java/**/${name}.@(java|kt)`) 22 )[0]; 23 assert( 24 filePath, 25 `Project file "${name}" does not exist in android project for root "${projectRoot}"` 26 ); 27 28 return filePath; 29} 30 31function getLanguage(filePath: string): 'java' | 'groovy' | 'kt' { 32 const extension = path.extname(filePath); 33 switch (extension) { 34 case '.java': 35 return 'java'; 36 case '.kts': 37 case '.kt': 38 return 'kt'; 39 case '.groovy': 40 case '.gradle': 41 return 'groovy'; 42 default: 43 throw new UnexpectedError(`Unexpected Android file extension: ${extension}`); 44 } 45} 46 47export function getFileInfo(filePath: string) { 48 return { 49 path: path.normalize(filePath), 50 contents: fs.readFileSync(filePath, 'utf8'), 51 language: getLanguage(filePath) as any, 52 }; 53} 54 55export async function getMainApplicationAsync( 56 projectRoot: string 57): Promise<ApplicationProjectFile> { 58 const filePath = getProjectFilePath(projectRoot, 'MainApplication'); 59 return getFileInfo(filePath); 60} 61 62export async function getMainActivityAsync(projectRoot: string): Promise<ApplicationProjectFile> { 63 const filePath = getProjectFilePath(projectRoot, 'MainActivity'); 64 return getFileInfo(filePath); 65} 66 67export function getGradleFilePath(projectRoot: string, gradleName: string): string { 68 const groovyPath = path.resolve(projectRoot, `${gradleName}.gradle`); 69 const ktPath = path.resolve(projectRoot, `${gradleName}.gradle.kts`); 70 71 const isGroovy = fs.existsSync(groovyPath); 72 const isKotlin = !isGroovy && fs.existsSync(ktPath); 73 74 if (!isGroovy && !isKotlin) { 75 throw new Error(`Failed to find '${gradleName}.gradle' file for project: ${projectRoot}.`); 76 } 77 const filePath = isGroovy ? groovyPath : ktPath; 78 return filePath; 79} 80 81export function getProjectBuildGradleFilePath(projectRoot: string): string { 82 return getGradleFilePath(path.join(projectRoot, 'android'), 'build'); 83} 84 85export async function getProjectBuildGradleAsync(projectRoot: string): Promise<GradleProjectFile> { 86 return getFileInfo(getProjectBuildGradleFilePath(projectRoot)); 87} 88 89export function getSettingsGradleFilePath(projectRoot: string): string { 90 return getGradleFilePath(path.join(projectRoot, 'android'), 'settings'); 91} 92 93export async function getSettingsGradleAsync(projectRoot: string): Promise<GradleProjectFile> { 94 return getFileInfo(getSettingsGradleFilePath(projectRoot)); 95} 96 97export function getAppBuildGradleFilePath(projectRoot: string): string { 98 return getGradleFilePath(path.join(projectRoot, 'android', 'app'), 'build'); 99} 100 101export async function getAppBuildGradleAsync(projectRoot: string): Promise<GradleProjectFile> { 102 return getFileInfo(getAppBuildGradleFilePath(projectRoot)); 103} 104 105export async function getProjectPathOrThrowAsync(projectRoot: string): Promise<string> { 106 const projectPath = path.join(projectRoot, 'android'); 107 if (await directoryExistsAsync(projectPath)) { 108 return projectPath; 109 } 110 throw new Error(`Android project folder is missing in project: ${projectRoot}`); 111} 112 113export async function getAndroidManifestAsync(projectRoot: string): Promise<string> { 114 const projectPath = await getProjectPathOrThrowAsync(projectRoot); 115 const filePath = path.join(projectPath, 'app/src/main/AndroidManifest.xml'); 116 return filePath; 117} 118 119export async function getResourceFolderAsync(projectRoot: string): Promise<string> { 120 const projectPath = await getProjectPathOrThrowAsync(projectRoot); 121 return path.join(projectPath, `app/src/main/res`); 122} 123 124export async function getResourceXMLPathAsync( 125 projectRoot: string, 126 { kind = 'values', name }: { kind?: ResourceKind; name: 'colors' | 'strings' | 'styles' | string } 127): Promise<string> { 128 const resourcePath = await getResourceFolderAsync(projectRoot); 129 130 const filePath = path.join(resourcePath, `${kind}/${name}.xml`); 131 return filePath; 132} 133