33import path = require( "path" ) ;
44import shell = require( "shelljs" ) ;
55import util = require( "util" ) ;
6+ import Future = require( "fibers/future" ) ;
67import options = require( "./../options" ) ;
78import constants = require( "./../constants" ) ;
89import hostInfo = require( "../common/host-info" ) ;
910import helpers = require( "./../common/helpers" ) ;
1011
1112class AndroidProjectService implements IPlatformProjectService {
13+ private SUPPORTED_TARGETS = [ "android-17" , "android-18" , "android-19" , "android-21" ] ;
1214 private targetApi : string ;
1315
1416 constructor ( private $androidEmulatorServices : Mobile . IEmulatorPlatformServices ,
@@ -50,7 +52,7 @@ class AndroidProjectService implements IPlatformProjectService {
5052 return ( ( ) => {
5153 this . $fs . ensureDirectoryExists ( projectRoot ) . wait ( ) ;
5254
53- this . validateAndroidTarget ( frameworkDir ) ; // We need framework to be installed to validate android target so we can't call this method in validate()
55+ var newTarget = this . validateAndroidTarget ( frameworkDir ) ; // We need framework to be installed to validate android target so we can't call this method in validate()
5456
5557 if ( options . symlink ) {
5658 this . copy ( projectRoot , frameworkDir , "res" , "-R" ) . wait ( ) ;
@@ -63,6 +65,10 @@ class AndroidProjectService implements IPlatformProjectService {
6365 this . copy ( projectRoot , frameworkDir , ".project AndroidManifest.xml project.properties" , "-f" ) . wait ( ) ;
6466 }
6567
68+ if ( newTarget ) {
69+ this . updateTarget ( projectRoot , newTarget ) . wait ( ) ;
70+ }
71+
6672 // Create src folder
6773 var packageName = this . $projectData . projectId ;
6874 var packageAsPath = packageName . replace ( / \. / g, path . sep ) ;
@@ -78,7 +84,6 @@ class AndroidProjectService implements IPlatformProjectService {
7884 var manifestPath = path . join ( projectRoot , "AndroidManifest.xml" ) ;
7985 var safeActivityName = this . $projectData . projectName . replace ( / \W / g, '' ) ;
8086 shell . sed ( '-i' , / _ _ P A C K A G E _ _ / , this . $projectData . projectId , manifestPath ) ;
81- shell . sed ( '-i' , / _ _ A C T I V I T Y _ _ / , safeActivityName , manifestPath ) ;
8287 shell . sed ( '-i' , / _ _ A P I L E V E L _ _ / , this . getTarget ( projectRoot ) . wait ( ) . split ( '-' ) [ 1 ] , manifestPath ) ;
8388
8489 var stringsFilePath = path . join ( projectRoot , 'res' , 'values' , 'strings.xml' ) ;
@@ -196,13 +201,58 @@ class AndroidProjectService implements IPlatformProjectService {
196201 }
197202 }
198203
199- private validateAndroidTarget ( frameworkDir : string ) {
204+ private validateAndroidTarget ( frameworkDir : string ) : string {
200205 var validTarget = this . getTarget ( frameworkDir ) . wait ( ) ;
201- var output = this . $childProcess . exec ( 'android list targets' ) . wait ( ) ;
202- if ( ! output . match ( validTarget ) ) {
203- 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." ,
204- validTarget . split ( '-' ) [ 1 ] ) ;
206+ var installedTargets = this . getInstalledTargets ( ) . wait ( ) ;
207+ var newTarget : string = undefined ;
208+ var match = _ . contains ( installedTargets , validTarget ) ;
209+ if ( ! match ) {
210+ // adjust to the latest available version
211+ newTarget = this . getCompatibleTarget ( ) ;
212+ if ( ! newTarget ) {
213+ this . $errors . fail ( "Please install Android target %s. Make sure you have the latest Android tools installed as well." +
214+ " Run \"android\" from your command-line to install/update any missing SDKs or tools." ,
215+ validTarget . split ( '-' ) [ 1 ] ) ;
216+ }
205217 }
218+
219+ return newTarget ;
220+ }
221+
222+ private getCompatibleTarget ( ) : string {
223+ var installedTargets = this . getInstalledTargets ( ) . wait ( ) ;
224+ var compatibleTarget = _ ( this . SUPPORTED_TARGETS ) . sort ( ) . findLast ( supportedTarget => _ . contains ( installedTargets , supportedTarget ) ) ;
225+ return compatibleTarget ;
226+ }
227+
228+ private updateTarget ( projectRoot : string , newTarget : string ) : IFuture < void > {
229+ return ( ( ) => {
230+ var file = path . join ( projectRoot , "project.properties" ) ;
231+ var editor = this . $propertiesParser . createEditor ( file ) . wait ( ) ;
232+ editor . set ( "target" , newTarget ) ;
233+ var future = new Future < void > ( ) ;
234+ editor . save ( ( err :any ) => {
235+ if ( err ) {
236+ future . throw ( err ) ;
237+ } else {
238+ this . targetApi = null ; // so that later we can repopulate the cache
239+ future . return ( ) ;
240+ }
241+ } ) ;
242+ future . wait ( ) ;
243+ } ) . future < void > ( ) ( ) ;
244+ }
245+
246+ private installedTargetsCache : string [ ] = null ;
247+ private getInstalledTargets ( ) : IFuture < string [ ] > {
248+ return ( ( ) => {
249+ if ( ! this . installedTargetsCache ) {
250+ this . installedTargetsCache = [ ] ;
251+ var output = this . $childProcess . exec ( 'android list targets' ) . wait ( ) ;
252+ output . replace ( / i d : \d + o r " ( .+ ) " / g, ( m :string , p1 :string ) => ( this . installedTargetsCache . push ( p1 ) , m ) ) ;
253+ }
254+ return this . installedTargetsCache ;
255+ } ) . future < string [ ] > ( ) ( ) ;
206256 }
207257
208258 private getTarget ( projectRoot : string ) : IFuture < string > {
0 commit comments