View Javadoc
1   package com.simpligility.maven.plugins.android.phase01generatesources;
2   
3   import com.android.builder.core.DefaultManifestParser;
4   import com.android.builder.symbols.RGeneration;
5   import com.android.builder.symbols.SymbolIo;
6   import com.android.builder.symbols.SymbolTable;
7   import org.apache.maven.artifact.Artifact;
8   import org.apache.maven.plugin.logging.Log;
9   
10  import java.io.File;
11  import java.util.ArrayList;
12  import java.util.List;
13  import java.util.Set;
14  
15  /**
16   * Generates R classes containing appropriate resource values for dependent libraries.
17   *
18   * @author William Ferguson - william.ferguson@xandar.com.au
19   */
20  final class ResourceClassGenerator
21  {
22      private final GenerateSourcesMojo mojo;
23      private final File targetDirectory;
24      private final File genDirectory;
25      private final Log log;
26      private final ClassLoader compileClassLoader;
27  
28      ResourceClassGenerator( final GenerateSourcesMojo mojo,
29                              final File targetDirectory,
30                              final File genDirectory,
31                              final ClassLoader compileClassLoader
32      )
33      {
34          this.mojo = mojo;
35          this.targetDirectory = targetDirectory;
36          this.genDirectory = genDirectory;
37          this.log = mojo.getLog();
38          this.compileClassLoader = compileClassLoader;
39      }
40  
41      /**
42       * Generates R java files those libraries that do not already have their R java in the compile classpath.
43       *
44       * If we are generating an integration test APK and the APK under test has a reference to a library for which
45       * it generated an R java, then we don't want to generate that R java again for the test APK because otherwise
46       * Proguard will fail when building the test APK because of duplication of the R java in the compile classpath.
47       *
48       * See {@link com.android.builder.core.AndroidBuilder#processResources(com.android.builder.internal.aapt.Aapt,
49       * com.android.builder.internal.aapt.AaptPackageConfig.Builder, boolean)}
50       *
51       * @param libraries AAR libraries for which to generate R java files.
52       */
53      public void generateLibraryRs( final Set<Artifact> libraries )
54      {
55          // list of all the symbol tables
56          final List<SymbolTable> symbolTables = new ArrayList<>( libraries.size() );
57  
58          // For each dependency, load its symbol file.
59          for ( final Artifact lib : libraries )
60          {
61              final File unpackedLibDirectory = mojo.getUnpackedLibFolder( lib );
62              final File rFile = new File( unpackedLibDirectory, "R.txt" );
63  
64              if ( rFile.isFile() )
65              {
66                  final File libManifestFile = new File( unpackedLibDirectory, "AndroidManifest.xml" );
67                  final String packageName = new DefaultManifestParser( libManifestFile ).getPackage();
68                  if ( rJavaAlreadyExists( packageName ) )
69                  {
70                      log.info( "Not creating R for " + packageName + " as it already exists" );
71                      continue;
72                  }
73                  log.info( "Generating R for " + packageName + " at " + rFile );
74  
75                  SymbolTable libSymbols = SymbolIo.read( rFile );
76                  libSymbols = libSymbols.rename( packageName, libSymbols.getTableName() );
77                  symbolTables.add( libSymbols );
78              }
79          }
80  
81          if ( symbolTables.isEmpty() )
82          {
83              return;
84          }
85  
86          // load the full resources values from the R.txt calculated for the project.
87          final File projectR = new File( targetDirectory, "R.txt" );
88          final SymbolTable mainSymbols = SymbolIo.read( projectR );
89  
90          // now loop on all the package name, merge all the symbols to write, and write them
91          RGeneration.generateRForLibraries( mainSymbols, symbolTables, genDirectory.getAbsoluteFile(), false );
92      }
93  
94      private boolean rJavaAlreadyExists( String packageName )
95      {
96          final String rJavaClass = packageName + ".R";
97          try
98          {
99              compileClassLoader.loadClass( rJavaClass );
100             return true;
101         }
102         catch ( ClassNotFoundException e )
103         {
104             log.debug( "Could not resolve R java : " + rJavaClass );
105             return false;
106         }
107     }
108 }