1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package com.simpligility.maven.plugins.android;
17
18 import com.android.SdkConstants;
19 import com.android.annotations.Nullable;
20 import com.android.repository.Revision;
21 import com.android.sdklib.AndroidTargetHash;
22 import com.android.sdklib.AndroidVersion;
23 import com.android.sdklib.BuildToolInfo;
24 import com.android.sdklib.IAndroidTarget;
25 import com.android.sdklib.repository.AndroidSdkHandler;
26 import com.android.sdklib.repository.targets.AndroidTargetManager;
27 import org.apache.maven.plugin.MojoExecutionException;
28
29 import java.io.File;
30 import java.io.FileInputStream;
31 import java.io.IOException;
32 import java.util.Properties;
33
34
35
36
37
38
39
40 public class AndroidSdk
41 {
42
43
44
45
46 private static final String DEFAULT_ANDROID_API_LEVEL = "23";
47
48
49
50 private static final String SOURCE_PROPERTIES_FILENAME = "source.properties";
51
52
53
54 private static final String SDK_TOOLS_REVISION_PROPERTY = "Pkg.Revision";
55
56
57
58
59 private static final String PLATFORMS_FOLDER_NAME = "platforms";
60
61 private static final String BIN_FOLDER_NAME_IN_TOOLS = "bin";
62
63 private static final String PARAMETER_MESSAGE = "Please provide a proper Android SDK directory path as "
64 + "configuration parameter <sdk><path>...</path></sdk> in the plugin <configuration/>. As an alternative,"
65 + " you may add the parameter to commandline: -Dandroid.sdk.path=... or set environment variable "
66 + AbstractAndroidMojo.ENV_ANDROID_HOME + ".";
67
68 private final File sdkPath;
69 private File platformToolsPath;
70 private File toolsPath;
71
72 private final IAndroidTarget androidTarget;
73 private AndroidSdkHandler sdkManager;
74 private int sdkMajorVersion;
75 private String buildToolsVersion;
76 private ProgressIndicatorImpl progressIndicator;
77
78 public AndroidSdk( File sdkPath, String apiLevel )
79 {
80 this( sdkPath, apiLevel, null );
81 }
82
83 public AndroidSdk( File sdkPath, String apiLevel, @Nullable String buildToolsVersion )
84 {
85 this.sdkPath = sdkPath;
86 this.buildToolsVersion = buildToolsVersion;
87 this.progressIndicator = new ProgressIndicatorImpl();
88
89 if ( sdkPath != null )
90 {
91 sdkManager = AndroidSdkHandler.getInstance( sdkPath );
92 platformToolsPath = new File( sdkPath, SdkConstants.FD_PLATFORM_TOOLS );
93 toolsPath = new File( sdkPath, SdkConstants.FD_TOOLS );
94
95 if ( sdkManager == null )
96 {
97 throw invalidSdkException( sdkPath, apiLevel );
98 }
99 }
100 loadSDKToolsMajorVersion();
101
102 if ( apiLevel == null )
103 {
104 apiLevel = DEFAULT_ANDROID_API_LEVEL;
105 }
106
107 androidTarget = findPlatformByApiLevel( apiLevel );
108 if ( androidTarget == null )
109 {
110 throw invalidSdkException( sdkPath, apiLevel );
111 }
112 }
113
114 private InvalidSdkException invalidSdkException( File sdkPath, String platformOrApiLevel )
115 {
116 throw new InvalidSdkException( "Invalid SDK: Platform/API level " + platformOrApiLevel
117 + " not available. This command should give you all you need:\n" + sdkPath.getAbsolutePath()
118 + File.separator + "tools" + File.separator + "android update sdk --no-ui --obsolete --force" );
119 }
120
121 private IAndroidTarget findPlatformByApiLevel( String apiLevel )
122 {
123
124 AndroidVersion version = null;
125 try
126 {
127 version = new AndroidVersion( apiLevel );
128 String hashString = AndroidTargetHash.getPlatformHashString( version );
129 IAndroidTarget target = sdkManager.getAndroidTargetManager( progressIndicator )
130 .getTargetFromHashString( hashString, progressIndicator );
131
132
133
134 if ( target != null && target.getLocation() != null )
135 {
136 return target;
137 }
138 }
139 catch ( AndroidVersion.AndroidVersionException ignore )
140 {
141 throw new InvalidSdkException( "Error AndroidVersion: " + ignore.getMessage() );
142 }
143
144
145 for ( IAndroidTarget t: sdkManager.getAndroidTargetManager( null ).getTargets( null ) )
146 {
147 if ( t.isPlatform() && apiLevel.equals( t.getVersionName() ) )
148 {
149 return t;
150 }
151 }
152 return null;
153 }
154
155 private void assertPathIsDirectory( final File path )
156 {
157 if ( path == null )
158 {
159 throw new InvalidSdkException( PARAMETER_MESSAGE );
160 }
161 if ( !path.isDirectory() )
162 {
163 throw new InvalidSdkException( "Path \"" + path + "\" is not a directory. " + PARAMETER_MESSAGE );
164 }
165 }
166
167
168
169
170
171
172 public String getAaptPath()
173 {
174 return getPathForBuildTool( BuildToolInfo.PathId.AAPT );
175 }
176
177
178
179
180
181 public String getAidlPath()
182 {
183 return getPathForBuildTool( BuildToolInfo.PathId.AIDL );
184 }
185
186
187
188
189
190 public String getDxJarPath()
191 {
192 return getPathForBuildTool( BuildToolInfo.PathId.DX_JAR );
193 }
194
195
196
197
198 public String getD8JarPath()
199 {
200 final File pathToDexJar = new File( getPathForBuildTool( BuildToolInfo.PathId.DX_JAR ) );
201 final File pathToD8Jar = new File( pathToDexJar.getParent(), "d8.jar" );
202 return pathToD8Jar.getAbsolutePath();
203 }
204
205
206
207
208
209 public String getProguardJarPath()
210 {
211 File directory = new File( getToolsPath(), "proguard" + File.separator + "lib" + File.separator );
212 File proguardJar = new File( directory, "proguard.jar" );
213 if ( proguardJar.exists() )
214 {
215 return proguardJar.getAbsolutePath();
216 }
217 throw new InvalidSdkException( "Cannot find " + proguardJar );
218 }
219
220
221
222
223
224 public String getShrinkedAndroidJarPath()
225 {
226 File shrinkedAndroidJar = new File( getBuildToolsLibDirectoryPath(), "shrinkedAndroid.jar" );
227 if ( shrinkedAndroidJar.exists() )
228 {
229 return shrinkedAndroidJar.getAbsolutePath();
230 }
231 throw new InvalidSdkException( "Cannot find " + shrinkedAndroidJar );
232 }
233
234
235
236
237
238 public String getBuildToolsLibDirectoryPath()
239 {
240 File buildToolsLib = new File( getBuildToolInfo().getLocation(), "lib" );
241 if ( buildToolsLib.exists() )
242 {
243 return buildToolsLib.getAbsolutePath();
244 }
245 throw new InvalidSdkException( "Cannot find " + buildToolsLib );
246 }
247
248
249
250
251
252 public String getMainDexClassesRulesPath()
253 {
254 File mainDexClassesRules = new File( getBuildToolInfo().getLocation(),
255 "mainDexClasses.rules" );
256 if ( mainDexClassesRules.exists() )
257 {
258 return mainDexClassesRules.getAbsolutePath();
259 }
260 throw new InvalidSdkException( "Cannot find " + mainDexClassesRules );
261 }
262
263 public void assertThatBuildToolsVersionIsAtLeast( String version, String feature )
264 throws InvalidSdkException, NumberFormatException
265 {
266 if ( getBuildToolInfo().getRevision().
267 compareTo( Revision.parseRevision( version ) ) < 0 )
268 {
269 throw new InvalidSdkException( "Version of build tools must be at least "
270 + version + " for " + feature + " to work" );
271 }
272 }
273
274
275
276
277
278
279 public String getAdbPath()
280 {
281 return getPathForPlatformTool( SdkConstants.FN_ADB );
282 }
283
284
285
286
287
288
289 public String getZipalignPath()
290 {
291 return getPathForBuildTool( BuildToolInfo.PathId.ZIP_ALIGN );
292 }
293
294
295
296
297
298
299 public String getLintPath()
300 {
301 return getPathForTool( BIN_FOLDER_NAME_IN_TOOLS + "/" + "lint" + ext( ".bat", "" ) );
302 }
303
304
305
306
307
308
309 public String getMonkeyRunnerPath()
310 {
311 return getPathForTool( BIN_FOLDER_NAME_IN_TOOLS + "/" + "monkeyrunner" + ext( ".bat", "" ) );
312 }
313
314
315
316
317
318
319 public String getApkBuilderPath()
320 {
321 return getPathForTool( "apkbuilder" + ext( ".bat", "" ) );
322 }
323
324
325
326
327
328
329 public String getAndroidPath()
330 {
331 String cmd = "android";
332 String ext = SdkConstants.currentPlatform() == 2 ? ".bat" : "";
333
334 return getPathForTool( cmd + ext );
335 }
336
337
338
339
340
341 public File getToolsPath()
342 {
343 return toolsPath;
344 }
345
346 private String getPathForBuildTool( BuildToolInfo.PathId pathId )
347 {
348 return getBuildToolInfo().getPath( pathId );
349 }
350
351 private BuildToolInfo getBuildToolInfo()
352 {
353
354 if ( buildToolsVersion != null && !buildToolsVersion.equals( "" ) )
355 {
356 BuildToolInfo buildToolInfo = sdkManager.getBuildToolInfo( Revision.parseRevision( buildToolsVersion ),
357 progressIndicator );
358 if ( buildToolInfo != null )
359 {
360 return buildToolInfo;
361 }
362
363
364 throw new InvalidSdkException( "Invalid SDK: Build-tools " + buildToolsVersion + " not found."
365 + " Check your Android SDK to install the build tools " + buildToolsVersion );
366 }
367
368 if ( androidTarget != null )
369 {
370 BuildToolInfo buildToolInfo = androidTarget.getBuildToolInfo();
371 if ( buildToolInfo != null )
372 {
373 return buildToolInfo;
374 }
375 }
376
377 BuildToolInfo latestBuildToolInfo = sdkManager.getLatestBuildTool( progressIndicator, true );
378 if ( latestBuildToolInfo == null )
379 {
380 throw new InvalidSdkException( "Invalid SDK: Build-tools not found. Check the content of '"
381 + sdkPath.getAbsolutePath() + File.separator + "build-tools', or run '"
382 + sdkPath.getAbsolutePath() + File.separator + "tools" + File.separator
383 + "android sdk' to install them" );
384 }
385 return latestBuildToolInfo;
386 }
387
388 private String getPathForPlatformTool( String tool )
389 {
390 return new File( platformToolsPath, tool ).getAbsolutePath();
391 }
392
393 private String getPathForTool( String tool )
394 {
395 return new File( toolsPath, tool ).getAbsolutePath();
396 }
397
398 private static String ext( String windowsExtension, String nonWindowsExtension )
399 {
400 if ( SdkConstants.currentPlatform() == SdkConstants.PLATFORM_WINDOWS )
401 {
402 return windowsExtension;
403 }
404 else
405 {
406 return nonWindowsExtension;
407 }
408 }
409
410
411
412
413
414
415 public String getPathForFrameworkAidl()
416 {
417 return androidTarget.getPath( IAndroidTarget.ANDROID_AIDL );
418 }
419
420
421
422
423
424
425
426
427 public File getAndroidJar() throws MojoExecutionException
428 {
429 final String androidJarPath = androidTarget.getPath( IAndroidTarget.ANDROID_JAR );
430 if ( androidJarPath == null )
431 {
432 throw new MojoExecutionException( "No AndroidJar found for " + androidTarget.getLocation() );
433 }
434 return new File ( androidJarPath );
435 }
436
437
438
439
440
441
442
443
444 public File getSdkPath() throws MojoExecutionException
445 {
446 if ( sdkPath.exists() )
447 {
448 return sdkPath;
449 }
450 throw new MojoExecutionException( "Can't find the SDK directory : " + sdkPath.getAbsolutePath() );
451 }
452
453
454
455
456
457 public File getPlatform()
458 {
459 assertPathIsDirectory( sdkPath );
460
461 final File platformsDirectory = new File( sdkPath, PLATFORMS_FOLDER_NAME );
462 assertPathIsDirectory( platformsDirectory );
463
464 final File platformDirectory;
465 if ( androidTarget == null )
466 {
467 IAndroidTarget latestTarget = null;
468 AndroidTargetManager targetManager = sdkManager.getAndroidTargetManager( progressIndicator );
469 for ( IAndroidTarget target: targetManager.getTargets( progressIndicator ) )
470 {
471 if ( target.isPlatform() )
472 {
473 if ( latestTarget == null
474 || target.getVersion().getApiLevel() > latestTarget.getVersion().getApiLevel() )
475 {
476 latestTarget = target;
477 }
478 }
479 }
480 platformDirectory = new File ( latestTarget.getLocation() );
481 }
482 else
483 {
484 platformDirectory = new File( androidTarget.getLocation() );
485 }
486 assertPathIsDirectory( platformDirectory );
487 return platformDirectory;
488 }
489
490
491
492
493 private void loadSDKToolsMajorVersion()
494 {
495 File propFile = new File( sdkPath, "tools/" + SOURCE_PROPERTIES_FILENAME );
496 Properties properties = new Properties();
497 try
498 {
499 properties.load( new FileInputStream( propFile ) );
500 }
501 catch ( IOException e )
502 {
503 throw new InvalidSdkException( "Error reading " + propFile.getAbsoluteFile() );
504 }
505
506 if ( properties.containsKey( SDK_TOOLS_REVISION_PROPERTY ) )
507 {
508 try
509 {
510 String versionString = properties.getProperty( SDK_TOOLS_REVISION_PROPERTY );
511 String majorVersion;
512 if ( versionString.matches( ".*[\\.| ].*" ) )
513 {
514 String[] versions = versionString.split( "[\\.| ]" );
515 majorVersion = versions[ 0 ];
516 }
517 else
518 {
519 majorVersion = versionString;
520 }
521 sdkMajorVersion = Integer.parseInt( majorVersion );
522 }
523 catch ( NumberFormatException e )
524 {
525 throw new InvalidSdkException( "Error - The property '" + SDK_TOOLS_REVISION_PROPERTY
526 + "' in the SDK source.properties file number is not an Integer: "
527 + properties.getProperty( SDK_TOOLS_REVISION_PROPERTY ) );
528 }
529 }
530 }
531
532
533
534
535
536
537 public int getSdkMajorVersion()
538 {
539 return sdkMajorVersion;
540 }
541 }