/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.ant; import com.android.apkbuilder.ApkBuilder; import com.android.apkbuilder.ApkBuilder.ApkFile; import com.android.sdklib.project.ApkConfigurationHelper; import com.android.sdklib.project.ProjectProperties; import com.android.sdklib.project.ProjectProperties.PropertyType; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; import org.apache.tools.ant.ProjectComponent; import org.apache.tools.ant.Task; import org.apache.tools.ant.types.Path; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.Map; import java.util.Set; import java.util.Map.Entry; public class ApkBuilderTask extends Task { /** * Class to represent nested elements. Since they all have only one attribute ('path'), the * same class can be used for all the nested elements (zip, file, sourcefolder, jarfolder, * nativefolder). */ public final static class Value extends ProjectComponent { String mPath; /** * Sets the value of the "path" attribute. * @param path the value. */ public void setPath(Path path) { mPath = path.toString(); } } private String mOutFolder; private String mBaseName; private boolean mVerbose = false; private boolean mSigned = true; private final ArrayList<Value> mZipList = new ArrayList<Value>(); private final ArrayList<Value> mFileList = new ArrayList<Value>(); private final ArrayList<Value> mSourceList = new ArrayList<Value>(); private final ArrayList<Value> mJarList = new ArrayList<Value>(); private final ArrayList<Value> mNativeList = new ArrayList<Value>(); private final ArrayList<FileInputStream> mZipArchives = new ArrayList<FileInputStream>(); private final ArrayList<File> mArchiveFiles = new ArrayList<File>(); private final ArrayList<ApkFile> mJavaResources = new ArrayList<ApkFile>(); private final ArrayList<FileInputStream> mResourcesJars = new ArrayList<FileInputStream>(); private final ArrayList<ApkFile> mNativeLibraries = new ArrayList<ApkFile>(); /** * Sets the value of the "outfolder" attribute. * @param outFolder the value. */ public void setOutfolder(Path outFolder) { mOutFolder = outFolder.toString(); } /** * Sets the value of the "basename" attribute. * @param baseName the value. */ public void setBasename(String baseName) { mBaseName = baseName; } /** * Sets the value of the "verbose" attribute. * @param verbose the value. */ public void setVerbose(boolean verbose) { mVerbose = verbose; } /** * Sets the value of the "signed" attribute. * @param signed the value. */ public void setSigned(boolean signed) { mSigned = signed; } /** * Returns an object representing a nested <var>zip</var> element. */ public Object createZip() { Value zip = new Value(); mZipList.add(zip); return zip; } /** * Returns an object representing a nested <var>file</var> element. */ public Object createFile() { Value file = new Value(); mFileList.add(file); return file; } /** * Returns an object representing a nested <var>sourcefolder</var> element. */ public Object createSourcefolder() { Value file = new Value(); mSourceList.add(file); return file; } /** * Returns an object representing a nested <var>jarfolder</var> element. */ public Object createJarfolder() { Value file = new Value(); mJarList.add(file); return file; } /** * Returns an object representing a nested <var>nativefolder</var> element. */ public Object createNativefolder() { Value file = new Value(); mNativeList.add(file); return file; } @Override public void execute() throws BuildException { Project taskProject = getProject(); ApkBuilder apkBuilder = new ApkBuilder(); apkBuilder.setVerbose(mVerbose); apkBuilder.setSignedPackage(mSigned); try { // setup the list of everything that needs to go in the archive. // go through the list of zip files to add. This will not include // the resource package, which is handled separaly for each apk to create. for (Value v : mZipList) { FileInputStream input = new FileInputStream(v.mPath); mZipArchives.add(input); } // now go through the list of file to directly add the to the list. for (Value v : mFileList) { mArchiveFiles.add(ApkBuilder.getInputFile(v.mPath)); } // now go through the list of file to directly add the to the list. for (Value v : mSourceList) { ApkBuilder.processSourceFolderForResource(v.mPath, mJavaResources); } // now go through the list of jar folders. for (Value v : mJarList) { ApkBuilder.processJarFolder(v.mPath, mResourcesJars); } // now the native lib folder. for (Value v : mNativeList) { String parameter = v.mPath; File f = new File(parameter); // compute the offset to get the relative path int offset = parameter.length(); if (parameter.endsWith(File.separator) == false) { offset++; } ApkBuilder.processNativeFolder(offset, f, mNativeLibraries); } // first do a full resource package createApk(apkBuilder, null /*configName*/, null /*resourceFilter*/); // now see if we need to create file with filtered resources. // Get the project base directory. File baseDir = taskProject.getBaseDir(); ProjectProperties properties = ProjectProperties.load(baseDir.getAbsolutePath(), PropertyType.DEFAULT); Map<String, String> apkConfigs = ApkConfigurationHelper.getConfigs(properties); if (apkConfigs.size() > 0) { Set<Entry<String, String>> entrySet = apkConfigs.entrySet(); for (Entry<String, String> entry : entrySet) { createApk(apkBuilder, entry.getKey(), entry.getValue()); } } } catch (FileNotFoundException e) { throw new BuildException(e); } catch (IllegalArgumentException e) { throw new BuildException(e); } } /** * Creates an application package. * @param apkBuilder * @param configName the name of the filter config. Can be null in which case a full resource * package will be generated. * @param resourceFilter the resource configuration filter to pass to aapt (if configName is * non null) * @throws FileNotFoundException */ private void createApk(ApkBuilder apkBuilder, String configName, String resourceFilter) throws FileNotFoundException { // All the files to be included in the archive have already been prep'ed up, except // the resource package. // figure out its name. String filename; if (configName != null && resourceFilter != null) { filename = mBaseName + "-" + configName + ".ap_"; } else { filename = mBaseName + ".ap_"; } // now we add it to the list of zip archive (it's just a zip file). // it's used as a zip archive input FileInputStream resoucePackageZipFile = new FileInputStream(new File(mOutFolder, filename)); mZipArchives.add(resoucePackageZipFile); // prepare the filename to generate. Same thing as the resource file. if (configName != null && resourceFilter != null) { filename = mBaseName + "-" + configName; } else { filename = mBaseName; } if (mSigned) { filename = filename + "-debug.apk"; } else { filename = filename + "-unsigned.apk"; } if (configName == null || resourceFilter == null) { if (mSigned) { System.out.println(String.format( "Creating %s and signing it with a debug key...", filename)); } else { System.out.println(String.format( "Creating %s for release...", filename)); } } else { if (mSigned) { System.out.println(String.format( "Creating %1$s (with %2$s) and signing it with a debug key...", filename, resourceFilter)); } else { System.out.println(String.format( "Creating %1$s (with %2$s) for release...", filename, resourceFilter)); } } File f = new File(mOutFolder, filename); // and generate the apk apkBuilder.createPackage(f.getAbsoluteFile(), mZipArchives, mArchiveFiles, mJavaResources, mResourcesJars, mNativeLibraries); // we are done. We need to remove the resource package from the list of zip archives // in case we have another apk to generate. mZipArchives.remove(resoucePackageZipFile); } }