1
+ ///<reference path="../.d.ts"/>
2
+ import path = require( "path" ) ;
3
+ import shell = require( "shelljs" ) ;
4
+ import options = require( "./../options" ) ;
5
+ import helpers = require( "./../common/helpers" ) ;
6
+
7
+ class AndroidProjectService implements IPlatformSpecificProjectService {
8
+ constructor ( private $fs : IFileSystem ,
9
+ private $errors : IErrors ,
10
+ private $logger : ILogger ,
11
+ private $childProcess : IChildProcess ,
12
+ private $projectData : IProjectData ,
13
+ private $propertiesParser : IPropertiesParser ) { }
14
+
15
+ public validate ( ) : IFuture < void > {
16
+ return ( ( ) => {
17
+ this . validatePackageName ( this . $projectData . projectId ) ;
18
+ this . validateProjectName ( this . $projectData . projectName ) ;
19
+
20
+ this . checkAnt ( ) . wait ( ) && this . checkAndroid ( ) . wait ( ) && this . checkJava ( ) . wait ( ) ;
21
+ } ) . future < void > ( ) ( ) ;
22
+ }
23
+
24
+ public createProject ( projectRoot : string , frameworkDir : string ) : IFuture < void > {
25
+ return ( ( ) => {
26
+ var packageName = this . $projectData . projectId ;
27
+ var packageAsPath = packageName . replace ( / \. / g, path . sep ) ;
28
+
29
+ var validTarget = this . getTarget ( frameworkDir ) . wait ( ) ;
30
+ var output = this . $childProcess . exec ( 'android list targets' ) . wait ( ) ;
31
+ if ( ! output . match ( validTarget ) ) {
32
+ this . $errors . fail ( "Please install Android target %s the Android newest SDK). Make sure you have the latest Android tools installed as well. Run \"android\" from your command-line to install/update any missing SDKs or tools." ,
33
+ validTarget . split ( '-' ) [ 1 ] ) ;
34
+ }
35
+
36
+ shell . cp ( "-r" , path . join ( frameworkDir , "assets" ) , projectRoot ) ;
37
+ shell . cp ( "-r" , path . join ( frameworkDir , "gen" ) , projectRoot ) ;
38
+ shell . cp ( "-r" , path . join ( frameworkDir , "libs" ) , projectRoot ) ;
39
+ shell . cp ( "-r" , path . join ( frameworkDir , "res" ) , projectRoot ) ;
40
+
41
+ shell . cp ( "-f" , path . join ( frameworkDir , ".project" ) , projectRoot ) ;
42
+ shell . cp ( "-f" , path . join ( frameworkDir , "AndroidManifest.xml" ) , projectRoot ) ;
43
+ shell . cp ( "-f" , path . join ( frameworkDir , "project.properties" ) , projectRoot ) ;
44
+
45
+ // Create src folder
46
+ var activityDir = path . join ( projectRoot , 'src' , packageAsPath ) ;
47
+ this . $fs . createDirectory ( activityDir ) . wait ( ) ;
48
+
49
+ } ) . future < any > ( ) ( ) ;
50
+ }
51
+
52
+ public interpolateData ( projectRoot : string ) : void {
53
+ // Interpolate the activity name and package
54
+ var stringsFilePath = path . join ( projectRoot , 'res' , 'values' , 'strings.xml' ) ;
55
+ shell . sed ( '-i' , / _ _ N A M E _ _ / , this . $projectData . projectName , stringsFilePath ) ;
56
+ shell . sed ( '-i' , / _ _ T I T L E _ A C T I V I T Y _ _ / , this . $projectData . projectName , stringsFilePath ) ;
57
+ shell . sed ( '-i' , / _ _ N A M E _ _ / , this . $projectData . projectName , path . join ( projectRoot , '.project' ) ) ;
58
+ shell . sed ( '-i' , / _ _ P A C K A G E _ _ / , this . $projectData . projectId , path . join ( projectRoot , "AndroidManifest.xml" ) ) ;
59
+ }
60
+
61
+ public executePlatformSpecificAction ( projectRoot : string ) {
62
+ var targetApi = this . getTarget ( projectRoot ) . wait ( ) ;
63
+ this . $logger . trace ( "Android target: %s" , targetApi ) ;
64
+ this . runAndroidUpdate ( projectRoot , targetApi ) . wait ( ) ;
65
+ }
66
+
67
+ public buildProject ( projectRoot : string ) : IFuture < void > {
68
+ return ( ( ) => {
69
+ var buildConfiguration = options . release ? "release" : "debug" ;
70
+ var args = this . getAntArgs ( buildConfiguration , projectRoot ) ;
71
+ this . spawn ( 'ant' , args ) ;
72
+ } ) . future < void > ( ) ( ) ;
73
+ }
74
+
75
+ private spawn ( command : string , args : string [ ] , options ?: any ) : void {
76
+ if ( helpers . isWindows ( ) ) {
77
+ args . unshift ( '/s' , '/c' , command ) ;
78
+ command = 'cmd' ;
79
+ }
80
+
81
+ this . $childProcess . spawn ( command , args , { cwd : options , stdio : 'inherit' } ) ;
82
+ }
83
+
84
+ private getAntArgs ( configuration : string , projectRoot : string ) : string [ ] {
85
+ var args = [ configuration , "-f" , path . join ( projectRoot , "build.xml" ) ] ;
86
+ return args ;
87
+ }
88
+
89
+ private runAndroidUpdate ( projectPath : string , targetApi : string ) : IFuture < void > {
90
+ return ( ( ) => {
91
+ var args = [
92
+ "--path" , projectPath ,
93
+ "--target" , targetApi
94
+ ] ;
95
+
96
+ this . spawn ( "android update project" , args ) ;
97
+ } ) . future < void > ( ) ( ) ;
98
+ }
99
+
100
+ private validatePackageName ( packageName : string ) : void {
101
+ //Make the package conform to Java package types
102
+ //Enforce underscore limitation
103
+ if ( ! / ^ [ a - z A - Z ] + ( \. [ a - z A - Z 0 - 9 ] [ a - z A - Z 0 - 9 _ ] * ) + $ / . test ( packageName ) ) {
104
+ this . $errors . fail ( "Package name must look like: com.company.Name" ) ;
105
+ }
106
+
107
+ //Class is a reserved word
108
+ if ( / \b [ C c ] l a s s \b / . test ( packageName ) ) {
109
+ this . $errors . fail ( "class is a reserved word" ) ;
110
+ }
111
+ }
112
+
113
+ private validateProjectName ( projectName : string ) : void {
114
+ if ( projectName === '' ) {
115
+ this . $errors . fail ( "Project name cannot be empty" ) ;
116
+ }
117
+
118
+ //Classes in Java don't begin with numbers
119
+ if ( / ^ [ 0 - 9 ] / . test ( projectName ) ) {
120
+ this . $errors . fail ( "Project name must not begin with a number" ) ;
121
+ }
122
+ }
123
+
124
+ private getTarget ( projectRoot : string ) : IFuture < string > {
125
+ return ( ( ) => {
126
+ var projectPropertiesFilePath = path . join ( projectRoot , "project.properties" ) ;
127
+
128
+ if ( this . $fs . exists ( projectPropertiesFilePath ) . wait ( ) ) {
129
+ var properties = this . $propertiesParser . createEditor ( projectPropertiesFilePath ) . wait ( ) ;
130
+ return properties . get ( "target" ) ;
131
+ }
132
+
133
+ return "" ;
134
+ } ) . future < string > ( ) ( ) ;
135
+ }
136
+
137
+ private checkAnt ( ) : IFuture < void > {
138
+ return ( ( ) => {
139
+ try {
140
+ this . $childProcess . exec ( "ant -version" ) . wait ( ) ;
141
+ } catch ( error ) {
142
+ this . $errors . fail ( "Error executing commands 'ant', make sure you have ant installed and added to your PATH." )
143
+ }
144
+ } ) . future < void > ( ) ( ) ;
145
+ }
146
+
147
+ private checkJava ( ) : IFuture < void > {
148
+ return ( ( ) => {
149
+ try {
150
+ this . $childProcess . exec ( "java -version" ) . wait ( ) ;
151
+ } catch ( error ) {
152
+ this . $errors . fail ( "%s\n Failed to run 'java', make sure your java environment is set up.\n Including JDK and JRE.\n Your JAVA_HOME variable is %s" , error , process . env . JAVA_HOME ) ;
153
+ }
154
+ } ) . future < void > ( ) ( ) ;
155
+ }
156
+
157
+ private checkAndroid ( ) : IFuture < void > {
158
+ return ( ( ) => {
159
+ try {
160
+ this . $childProcess . exec ( 'android list targets' ) . wait ( ) ;
161
+ } catch ( error ) {
162
+ if ( error . match ( / c o m m a n d \s n o t \s f o u n d / ) ) {
163
+ this . $errors . fail ( "The command \"android\" failed. Make sure you have the latest Android SDK installed, and the \"android\" command (inside the tools/ folder) is added to your path." ) ;
164
+ } else {
165
+ this . $errors . fail ( "An error occurred while listing Android targets" ) ;
166
+ }
167
+ }
168
+ } ) . future < void > ( ) ( ) ;
169
+ }
170
+ }
171
+ $injector . register ( "androidProjectService" , AndroidProjectService ) ;
0 commit comments