/**
   * Given a map of source directories to list of AIDL (relative) filenames within each, runs the
   * AIDL compiler for each, such that all source directories are available to the AIDL compiler.
   *
   * @param files Map of source directory File instances to the relative paths to all AIDL files
   *     within
   * @throws MojoExecutionException If the AIDL compiler fails
   */
  private void generateAidlFiles(
      Map<File /*sourceDirectory*/, String[] /*relativeAidlFileNames*/> files)
      throws MojoExecutionException {
    List<String> protoCommands = new ArrayList<String>();
    protoCommands.add("-p" + getAndroidSdk().getPathForFrameworkAidl());

    genDirectoryAidl.mkdirs();
    getLog().info("Adding AIDL gen folder to compile classpath: " + genDirectoryAidl);
    project.addCompileSourceRoot(genDirectoryAidl.getPath());
    Set<File> sourceDirs = files.keySet();
    for (File sourceDir : sourceDirs) {
      protoCommands.add("-I" + sourceDir);
    }
    for (File sourceDir : sourceDirs) {
      for (String relativeAidlFileName : files.get(sourceDir)) {
        File targetDirectory =
            new File(genDirectoryAidl, new File(relativeAidlFileName).getParent());
        targetDirectory.mkdirs();

        final String shortAidlFileName = new File(relativeAidlFileName).getName();
        final String shortJavaFileName =
            shortAidlFileName.substring(0, shortAidlFileName.lastIndexOf(".")) + ".java";
        final File aidlFileInSourceDirectory = new File(sourceDir, relativeAidlFileName);

        List<String> commands = new ArrayList<String>(protoCommands);
        commands.add(aidlFileInSourceDirectory.getAbsolutePath());
        commands.add(new File(targetDirectory, shortJavaFileName).getAbsolutePath());
        try {
          CommandExecutor executor = CommandExecutor.Factory.createDefaultCommmandExecutor();
          executor.setLogger(this.getLog());
          executor.setCaptureStdOut(true);
          executor.executeCommand(
              getAndroidSdk().getAidlPath(), commands, project.getBasedir(), false);
        } catch (ExecutionException e) {
          throw new MojoExecutionException("", e);
        }
      }
    }
  }
  /**
   * Executes aapt to generate the R class for the given apklib.
   *
   * @param apklibArtifact apklib for which to generate the R class.
   * @throws MojoExecutionException if it fails.
   */
  private void generateRForApkLibDependency(Artifact apklibArtifact) throws MojoExecutionException {
    final File unpackDir = getUnpackedLibFolder(apklibArtifact);
    getLog()
        .debug(
            "Generating incomplete R file for apklib: "
                + apklibArtifact.getGroupId()
                + ":"
                + apklibArtifact.getArtifactId());
    final File apklibManifest = new File(unpackDir, "AndroidManifest.xml");
    final File apklibResDir = new File(unpackDir, "res");

    List<File> dependenciesResDirectories = new ArrayList<File>();
    final Set<Artifact> apklibDeps =
        getDependencyResolver().getLibraryDependenciesFor(project, apklibArtifact);
    getLog().debug("apklib=" + apklibArtifact + "  dependencies=" + apklibDeps);
    for (Artifact dependency : apklibDeps) {
      // Add in the resources that are dependencies of the apklib.
      final String extension = dependency.getType();
      final File dependencyResDir = getUnpackedLibResourceFolder(dependency);
      if ((extension.equals(APKLIB) || extension.equals(AAR)) && dependencyResDir.exists()) {
        dependenciesResDirectories.add(dependencyResDir);
      }
    }

    // Create combinedAssets for this apklib dependency - can't have multiple -A args
    final File apklibCombAssets = new File(getUnpackedLibFolder(apklibArtifact), "combined-assets");
    for (Artifact dependency : apklibDeps) {
      // Accumulate assets for dependencies of the apklib (if they exist).
      final String extension = dependency.getType();
      final File dependencyAssetsDir = getUnpackedLibAssetsFolder(dependency);
      if ((extension.equals(APKLIB) || extension.equals(AAR))) {
        copyFolder(dependencyAssetsDir, apklibCombAssets);
      }
    }
    // Overlay the apklib dependency assets (if they exist)
    final File apkLibAssetsDir = getUnpackedLibAssetsFolder(apklibArtifact);
    copyFolder(apkLibAssetsDir, apklibCombAssets);

    final CommandExecutor executor = CommandExecutor.Factory.createDefaultCommmandExecutor();
    executor.setLogger(getLog());

    AaptCommandBuilder commandBuilder =
        new AaptCommandBuilder()
            .packageResources()
            .makeResourcesNonConstant()
            .makePackageDirectories()
            .setWhereToOutputResourceConstants(genDirectory.getAbsolutePath())
            .generateRIntoPackage(extractPackageNameFromAndroidManifest(apklibManifest))
            .setPathToAndroidManifest(apklibManifest.getAbsolutePath())
            .addResourceDirectoryIfExists(apklibResDir)
            .addResourceDirectoriesIfExists(dependenciesResDirectories)
            .autoAddOverlay()
            .addRawAssetsDirectoryIfExists(apklibCombAssets)
            .addExistingPackageToBaseIncludeSet(getAndroidSdk().getAndroidJar().getAbsolutePath())
            .addConfigurations(configurations)
            .addExtraArguments(aaptExtraArgs)
            .setVerbose(aaptVerbose)
            // We need to generate R.txt for all projects as it needs to be consumed when generating
            // R class.
            // It also needs to be consumed when packaging aar.
            .generateRTextFile(unpackDir.getAbsolutePath());

    getLog().debug(getAndroidSdk().getAaptPath() + " " + commandBuilder.toString());
    try {
      executor.setCaptureStdOut(true);
      List<String> commands = commandBuilder.build();
      executor.executeCommand(getAndroidSdk().getAaptPath(), commands, project.getBasedir(), false);
    } catch (ExecutionException e) {
      throw new MojoExecutionException("", e);
    }
  }
  private void generateR() throws MojoExecutionException {
    getLog().info("Generating R file for " + project.getArtifact());

    genDirectory.mkdirs();

    final File[] overlayDirectories = getResourceOverlayDirectories();
    getLog().debug("Resource overlay folders : " + Arrays.asList(overlayDirectories));

    final CommandExecutor executor = CommandExecutor.Factory.createDefaultCommmandExecutor();
    executor.setLogger(this.getLog());

    final List<String> commands = new ArrayList<String>();
    commands.add("package");

    commands.add("-f");
    commands.add("--no-crunch");

    // inputs
    commands.add("-I");
    commands.add(getAndroidSdk().getAndroidJar().getAbsolutePath());

    commands.add("-M");
    commands.add(androidManifestFile.getAbsolutePath());

    // NB AndroidBuilder only adds a single folder - presumably it contains a merge of all
    // resources.
    for (File resOverlayDir : overlayDirectories) {
      if (resOverlayDir != null && resOverlayDir.exists()) {
        getLog().debug("Adding resource overlay folder : " + resOverlayDir);
        commands.add("-S");
        commands.add(resOverlayDir.getAbsolutePath());
      }
    }
    if (resourceDirectory.exists()) {
      getLog().debug("Adding resource folder : " + resourceDirectory);
      commands.add("-S");
      commands.add(resourceDirectory.getAbsolutePath());
    }

    // Need to include any AAR or APKLIB dependencies when generating R because if any local
    // resources directly reference dependent resources then R generation will crash.
    addLibraryResourceFolders(commands);

    // NB aapt only accepts a single assets parameter - combinedAssets is a merge of all assets
    if (combinedAssets.exists()) {
      getLog().debug("Adding assets folder : " + combinedAssets);
      commands.add("-A");
      commands.add(combinedAssets.getAbsolutePath());
    }

    // outputs
    commands.add("-m");
    commands.add("-J");
    commands.add(genDirectory.getAbsolutePath());

    // Write the output to an optional location
    // Used by AndroidBuilder but not by us.
    // final File optionalOutputLocation = some file;
    // getLog().debug( "Using default package : " + optionalOutputLocation );
    // commands.add( "-F" );
    // commands.add( optionalOutputLocation.getAbsolutePath() );

    // If a proguard file is defined then output Proguard options to it.
    if (proguardFile != null) {
      final File parentFolder = proguardFile.getParentFile();
      if (parentFolder != null) {
        parentFolder.mkdirs();
      }
      getLog().debug("Adding proguard file : " + proguardFile);
      commands.add("-G");
      commands.add(proguardFile.getAbsolutePath());
    }

    if (StringUtils.isNotBlank(customPackage)) {
      getLog().debug("Adding custom-package : " + customPackage);
      commands.add("--custom-package");
      commands.add(customPackage);
    }

    if (AAR.equals(project.getArtifact().getType())) {
      getLog().debug("Adding non-constant-id");
      commands.add("--non-constant-id");
    }

    for (String aaptExtraArg : aaptExtraArgs) {
      getLog().debug("Adding aapt arg : " + aaptExtraArg);
      commands.add(aaptExtraArg);
    }

    if (StringUtils.isNotBlank(configurations)) {
      // Should be comma separated list of locales etc.
      getLog().debug("Adding resource configurations : " + configurations);
      commands.add("-c");
      commands.add(configurations);
    }

    if (aaptVerbose) {
      commands.add("-v");
    }

    // We need to generate R.txt for all projects as it needs to be consumed when generating R
    // class.
    // It also needs to be consumed when packaging aar.
    commands.add("--output-text-symbols");
    commands.add(targetDirectory.getAbsolutePath());

    // Allows us to supply multiple -S arguments.
    commands.add("--auto-add-overlay");

    getLog().debug(getAndroidSdk().getAaptPath() + " " + commands.toString());
    try {
      targetDirectory.mkdirs();
      executor.setCaptureStdOut(true);
      executor.executeCommand(getAndroidSdk().getAaptPath(), commands, project.getBasedir(), false);
    } catch (ExecutionException e) {
      throw new MojoExecutionException("", e);
    }

    ResourceClassGenerator resourceGenerator =
        new ResourceClassGenerator(this, targetDirectory, genDirectory);

    generateCorrectRJavaForApklibDependencies(resourceGenerator);
    generateCorrectRJavaForAarDependencies(resourceGenerator);

    getLog().info("Adding R gen folder to compile classpath: " + genDirectory);
    project.addCompileSourceRoot(genDirectory.getAbsolutePath());
  }
  /**
   * Generates an intermediate apk file (actually .ap_) containing the resources and assets.
   *
   * @throws MojoExecutionException
   */
  private void generateIntermediateApk() throws MojoExecutionException {
    CommandExecutor executor = CommandExecutor.Factory.createDefaultCommmandExecutor();
    executor.setLogger(this.getLog());
    File[] overlayDirectories = getResourceOverlayDirectories();

    File androidJar = getAndroidSdk().getAndroidJar();
    File outputFile =
        new File(project.getBuild().getDirectory(), project.getBuild().getFinalName() + ".ap_");

    List<String> commands = new ArrayList<String>();
    commands.add("package");
    commands.add("-f");
    commands.add("-M");
    commands.add(androidManifestFile.getAbsolutePath());
    for (File resOverlayDir : overlayDirectories) {
      if (resOverlayDir != null && resOverlayDir.exists()) {
        commands.add("-S");
        commands.add(resOverlayDir.getAbsolutePath());
      }
    }
    if (resourceDirectory.exists()) {
      commands.add("-S");
      commands.add(resourceDirectory.getAbsolutePath());
    }
    for (Artifact libraryArtifact : getTransitiveDependencyArtifacts(APKLIB, AAR)) {
      final File libraryResDir = getUnpackedLibResourceFolder(libraryArtifact);
      if (libraryResDir.exists()) {
        commands.add("-S");
        commands.add(libraryResDir.getAbsolutePath());
      }
    }
    commands.add("--auto-add-overlay");

    // NB aapt only accepts a single assets parameter - combinedAssets is a merge of all assets
    if (combinedAssets.exists()) {
      getLog().debug("Adding assets folder : " + combinedAssets);
      commands.add("-A");
      commands.add(combinedAssets.getAbsolutePath());
    }

    if (StringUtils.isNotBlank(renameManifestPackage)) {
      commands.add("--rename-manifest-package");
      commands.add(renameManifestPackage);
    }

    if (StringUtils.isNotBlank(renameInstrumentationTargetPackage)) {
      commands.add("--rename-instrumentation-target-package");
      commands.add(renameInstrumentationTargetPackage);
    }

    commands.add("-I");
    commands.add(androidJar.getAbsolutePath());
    commands.add("-F");
    commands.add(outputFile.getAbsolutePath());
    if (StringUtils.isNotBlank(configurations)) {
      commands.add("-c");
      commands.add(configurations);
    }

    for (String aaptExtraArg : aaptExtraArgs) {
      commands.add(aaptExtraArg);
    }

    if (aaptVerbose) {
      commands.add("-v");
    }

    if (!release) {
      getLog().info("Generating debug apk.");
      commands.add("--debug-mode");
    } else {
      getLog().info("Generating release apk.");
    }

    getLog().debug(getAndroidSdk().getAaptPath() + " " + commands.toString());
    try {
      executor.setCaptureStdOut(true);
      executor.executeCommand(getAndroidSdk().getAaptPath(), commands, project.getBasedir(), false);
    } catch (ExecutionException e) {
      throw new MojoExecutionException("", e);
    }
  }