diff options
9 files changed, 426 insertions, 67 deletions
diff --git a/anttasks/src/com/android/ant/MultiApkExportTask.java b/anttasks/src/com/android/ant/MultiApkExportTask.java index 1abbd57..03e27e9 100644 --- a/anttasks/src/com/android/ant/MultiApkExportTask.java +++ b/anttasks/src/com/android/ant/MultiApkExportTask.java @@ -92,7 +92,7 @@ public class MultiApkExportTask extends Task { File rootFolder = antProject.getBaseDir(); MultiApkExportHelper helper = new MultiApkExportHelper(rootFolder.getAbsolutePath(), - appPackage, versionCode, mTarget); + appPackage, versionCode, mTarget, System.out); try { if (mTarget == Target.CLEAN) { @@ -305,7 +305,6 @@ public class MultiApkExportTask extends Task { // set the resource pack file name. addProp(subAnt, "resource.package.file.name", pkgName + ".ap_"); - if (canSign) { // set the properties for the password. addProp(subAnt, ProjectProperties.PROPERTY_KEY_STORE, keyStore); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml b/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml index 78ebac3..13cf652 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml +++ b/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml @@ -207,16 +207,21 @@ <separator name="group1"/> </menu> <visibility> - <not> - <or> - <objectState - name="projectNature" - value="com.android.ide.eclipse.adt.AndroidNature"/> - <objectState - name="open" - value="false"/> - </or> - </not> + <and> + <not> + <objectState + name="projectNature" + value="com.android.ide.eclipse.adt.AndroidNature"/> + </not> + <not> + <objectState + name="projectNature" + value="com.android.ide.eclipse.adt.AndroidExportNature"/> + </not> + <objectState + name="open" + value="true"/> + </and> </visibility> <action class="com.android.ide.eclipse.adt.internal.actions.ConvertToAndroidAction" @@ -284,7 +289,31 @@ label="Rename Application Package" menubarPath="com.android.ide.eclipse.adt.AndroidTools/group3"/> </objectContribution> - + <objectContribution + id="com.android.ide.eclipse.adt.contribution3" + nameFilter="*" + objectClass="org.eclipse.core.resources.IProject" + adaptable="true"> + <menu + id="com.android.ide.eclipse.adt.AndroidTools" + label="Android Tools" + path="additions"> + <separator name="group1"/> + <separator name="group2"/> + </menu> + <filter + name="projectNature" + value="com.android.ide.eclipse.adt.AndroidExportNature"> + </filter> + <action + class="com.android.ide.eclipse.adt.internal.actions.MultiApkExportAction" + enablesFor="1" + id="com.android.ide.eclipse.adt.actions.MultiApkExportAction" + label="Export APKs" + menubarPath="com.android.ide.eclipse.adt.AndroidTools/group1" + tooltip="Exports multiple APKs from the export project configuration"> + </action> + </objectContribution> </extension> <extension point="org.eclipse.ui.preferencePages"> diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/ConvertToAndroidAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/ConvertToAndroidAction.java index ad9fce0..92604fb 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/ConvertToAndroidAction.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/ConvertToAndroidAction.java @@ -84,7 +84,7 @@ public class ConvertToAndroidAction implements IObjectActionDelegate { * @see IActionDelegate#selectionChanged(IAction, ISelection) */ public void selectionChanged(IAction action, ISelection selection) { - this.mSelection = selection; + mSelection = selection; } /** diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/FixProjectAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/FixProjectAction.java index cb4f7e7..8a2400b 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/FixProjectAction.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/FixProjectAction.java @@ -75,7 +75,7 @@ public class FixProjectAction implements IObjectActionDelegate { } public void selectionChanged(IAction action, ISelection selection) { - this.mSelection = selection; + mSelection = selection; } private void fixProject(final IProject project) { @@ -92,7 +92,7 @@ public class FixProjectAction implements IObjectActionDelegate { if (monitor != null) { monitor.worked(1); } - + // fix the nature order to have the proper project icon ProjectHelper.fixProjectNatureOrder(project); if (monitor != null) { @@ -114,7 +114,7 @@ public class FixProjectAction implements IObjectActionDelegate { if (monitor != null) { monitor.worked(1); } - + return Status.OK_STATUS; } catch (JavaModelException e) { return e.getJavaModelStatus(); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/MultiApkExportAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/MultiApkExportAction.java new file mode 100644 index 0000000..fe05b1d --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/MultiApkExportAction.java @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.ide.eclipse.adt.internal.actions; + +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.AndroidConstants; +import com.android.ide.eclipse.adt.internal.build.ApkBuilderHelper; +import com.android.ide.eclipse.adt.internal.project.ProjectHelper; +import com.android.ide.eclipse.adt.internal.project.ProjectState; +import com.android.ide.eclipse.adt.internal.sdk.Sdk; +import com.android.ide.eclipse.adt.io.IFolderWrapper; +import com.android.sdklib.SdkConstants; +import com.android.sdklib.internal.export.ApkData; +import com.android.sdklib.internal.export.MultiApkExportHelper; +import com.android.sdklib.internal.export.ProjectConfig; +import com.android.sdklib.internal.export.MultiApkExportHelper.ExportException; +import com.android.sdklib.internal.export.MultiApkExportHelper.Target; +import com.android.sdklib.internal.project.ProjectProperties; +import com.android.sdklib.internal.project.ProjectProperties.PropertyType; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.IncrementalProjectBuilder; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.ui.IObjectActionDelegate; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.PlatformUI; + +import java.io.File; +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +/** + * Multiple APK export Action. + * The action is triggered on a project selection, and performs a full APK export based on the + * content of the export.properties file. + */ +public class MultiApkExportAction implements IObjectActionDelegate { + + private ISelection mSelection; + + public void setActivePart(IAction action, IWorkbenchPart targetPart) { + // pass + } + + public void run(IAction action) { + if (mSelection instanceof IStructuredSelection) { + for (Iterator<?> it = ((IStructuredSelection)mSelection).iterator(); it.hasNext();) { + Object element = it.next(); + IProject project = null; + if (element instanceof IProject) { + project = (IProject)element; + } else if (element instanceof IAdaptable) { + project = (IProject)((IAdaptable)element).getAdapter(IProject.class); + } + if (project != null) { + IWorkbench workbench = PlatformUI.getWorkbench(); + final IProject fProject = project; + try { + workbench.getProgressService().busyCursorWhile(new IRunnableWithProgress() { + /** + * Run the export. + * @throws InvocationTargetException + * @throws InterruptedException + */ + public void run(IProgressMonitor monitor) + throws InvocationTargetException, InterruptedException { + try { + runMultiApkExport(fProject, monitor); + } catch (Exception e) { + AdtPlugin.logAndPrintError(e, fProject.getName(), + "Failed to export project: %1$s", e.getMessage()); + } finally { + monitor.done(); + } + } + }); + } catch (Exception e) { + AdtPlugin.logAndPrintError(e, project.getName(), + "Failed to export project: %1$s", + e.getMessage()); + } + } + } + } + } + + public void selectionChanged(IAction action, ISelection selection) { + mSelection = selection; + } + + /** + * Runs the multi-apk export. + * @param exportProject the main "export" project. + * @param monitor the progress monitor. + * @throws ExportException + * @throws CoreException + */ + private void runMultiApkExport(IProject exportProject, IProgressMonitor monitor) + throws ExportException, CoreException { + + ProjectProperties props = ProjectProperties.load(new IFolderWrapper(exportProject), + PropertyType.EXPORT); + + // get some props and make sure their values are valid. + + String appPackage = props.getProperty(ProjectProperties.PROPERTY_PACKAGE); + if (appPackage == null || appPackage.length() == 0) { + throw new IllegalArgumentException("Invalid 'package' property values."); + } + + String version = props.getProperty(ProjectProperties.PROPERTY_VERSIONCODE); + int versionCode; + try { + versionCode = Integer.parseInt(version); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("version value is not a valid integer.", e); + } + + String projects = props.getProperty(ProjectProperties.PROPERTY_PROJECTS); + if (projects == null || projects.length() == 0) { + throw new IllegalArgumentException("Missing project list."); + } + + // create the multi apk helper to get the list of apk to export. + MultiApkExportHelper helper = new MultiApkExportHelper( + exportProject.getLocation().toOSString(), + appPackage, versionCode, Target.RELEASE, System.out); + + List<ApkData> apks = helper.getApkData(projects); + + // list of projects that have been resolved (ie the IProject has been found from the + // ProjectConfig) and compiled. + HashMap<ProjectConfig, ProjectState> resolvedProjects = + new HashMap<ProjectConfig, ProjectState>(); + + IWorkspace ws = ResourcesPlugin.getWorkspace(); + IWorkspaceRoot wsRoot = ws.getRoot(); + + // bin folder for the export project + IFolder binFolder = exportProject.getFolder(SdkConstants.FD_OUTPUT); + if (binFolder.exists() == false) { + binFolder.create(true, true, monitor); + } + + for (ApkData apk : apks) { + // find the IProject object for this apk. + ProjectConfig projectConfig = apk.getProjectConfig(); + ProjectState projectState = resolvedProjects.get(projectConfig); + if (projectState == null) { + // first time? resolve the project and compile it. + IPath path = exportProject.getFullPath().append(projectConfig.getRelativePath()); + + IResource res = wsRoot.findMember(path); + if (res.getType() != IResource.PROJECT) { + throw new IllegalArgumentException(String.format( + "%1$s does not resolve to a project.", + projectConfig.getRelativePath())); + } + + IProject project = (IProject)res; + + projectState = Sdk.getProjectState(project); + if (projectState == null) { + throw new IllegalArgumentException(String.format( + "State for project %1$s could not be loaded.", + project.getName())); + } + + if (projectState.isLibrary()) { + throw new IllegalArgumentException(String.format( + "Project %1$s is a library and cannot be part of a multi-apk export.", + project.getName())); + } + + // build the project, mainly for the java compilation. The rest is handled below. + project.build(IncrementalProjectBuilder.INCREMENTAL_BUILD, monitor); + + // store the resolved project in the map. + resolvedProjects.put(projectConfig, projectState); + } + + Map<String, String> variantMap = apk.getSoftVariantMap(); + + if (variantMap.size() > 0) { + // if there are soft variants, only export those. + for (Entry<String, String> entry : variantMap.entrySet()) { + buildVariant(wsRoot, projectState, appPackage, versionCode, apk, entry, + binFolder); + } + } else { + buildVariant(wsRoot, projectState, appPackage, versionCode, apk, + null /*soft variant*/, binFolder); + } + } + + helper.writeLogs(); + } + + /** + * Builds a particular variant of an APK + * @param wsRoot the workspace root + * @param projectState the project to export + * @param appPackage the application package + * @param versionCode the major version code. + * @param apk the {@link ApkData} describing how the export should happen. + * @param softVariant an optional soft variant info. The entry contains (name, resource filter). + * @param binFolder the binFolder where the file must be created. + * @throws CoreException + */ + private void buildVariant(IWorkspaceRoot wsRoot, ProjectState projectState, String appPackage, + int versionCode, ApkData apk, Entry<String, String> softVariant, IFolder binFolder) + throws CoreException { + // get the libraries for this project + IProject[] libProjects = projectState.getLibraryProjects(); + + IProject project = projectState.getProject(); + IJavaProject javaProject = JavaCore.create(project); + + int compositeVersionCode = apk.getCompositeVersionCode(versionCode); + + // figure out the file names + String pkgName = project.getName() + "-" + apk.getBuildInfo(); + String finalNameRoot = appPackage + "-" + compositeVersionCode; + if (softVariant != null) { + String tmp = "-" + softVariant.getKey(); + pkgName += tmp; + finalNameRoot += tmp; + } + + pkgName += ".ap_"; + String outputName = finalNameRoot + "-unsigned.apk"; + + ApkBuilderHelper helper = new ApkBuilderHelper(project, System.out, System.err); + + // get the manifest file + IFile manifestFile = project.getFile(SdkConstants.FN_ANDROID_MANIFEST_XML); + // get the project bin folder + IFolder projectBinFolder = wsRoot.getFolder(javaProject.getOutputLocation()); + String projectBinFolderPath = projectBinFolder.getLocation().toOSString(); + + // package the resources + if (helper.packageResources(manifestFile, libProjects, + softVariant != null ? softVariant.getValue() : null, compositeVersionCode, + projectBinFolderPath, pkgName) == false) { + return; + } + + apk.setOutputName(softVariant != null ? softVariant.getKey() : null, outputName); + + // do the final export. + IFile dexFile = projectBinFolder.getFile(AndroidConstants.FN_CLASSES_DEX); + String outputFile = binFolder.getFile(outputName).getLocation().toOSString(); + + // get the list of referenced projects. + IProject[] javaRefs = ProjectHelper.getReferencedProjects(project); + IJavaProject[] referencedJavaProjects = ApkBuilderHelper.getJavaProjects(javaRefs); + + helper.finalPackage(new File(projectBinFolderPath, pkgName).getAbsolutePath(), + dexFile.getLocation().toOSString(), + outputFile, javaProject, libProjects, referencedJavaProjects, + apk.getAbi(), false /*debuggable*/); + + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/RenamePackageAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/RenamePackageAction.java index e8f8ed0..f06e00d 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/RenamePackageAction.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/RenamePackageAction.java @@ -126,11 +126,11 @@ public class RenamePackageAction implements IObjectActionDelegate { * @see IObjectActionDelegate#setActivePart(IAction, IWorkbenchPart) */ public void setActivePart(IAction action, IWorkbenchPart targetPart) { - this.mTargetPart = targetPart; + mTargetPart = targetPart; } public void selectionChanged(IAction action, ISelection selection) { - this.mSelection = selection; + mSelection = selection; } /** @@ -184,7 +184,7 @@ public class RenamePackageAction implements IObjectActionDelegate { final String old_package_name_string = manifestData.getPackage(); final AST ast_validator = AST.newAST(AST.JLS3); - this.mOldPackageName = ast_validator.newName(old_package_name_string); + mOldPackageName = ast_validator.newName(old_package_name_string); IInputValidator validator = new IInputValidator() { @@ -207,7 +207,7 @@ public class RenamePackageAction implements IObjectActionDelegate { validator); if (dialog.open() == Window.OK) { - this.mNewPackageName = ast_validator.newName(dialog.getValue()); + mNewPackageName = ast_validator.newName(dialog.getValue()); initiateAndroidPackageRefactoring(project); } } @@ -565,8 +565,8 @@ public class RenamePackageAction implements IObjectActionDelegate { final ASTRewrite mRewriter; ImportVisitor(AST ast) { - this.mAst = ast; - this.mRewriter = ASTRewrite.create(ast); + mAst = ast; + mRewriter = ASTRewrite.create(ast); } public TextEdit getTextEdit() { @@ -591,7 +591,7 @@ public class RenamePackageAction implements IObjectActionDelegate { if (qualified_import_name.getName().getIdentifier() .equals(AndroidConstants.FN_RESOURCE_BASE)) { - this.mRewriter.replace(qualified_import_name.getQualifier(), mNewPackageName, + mRewriter.replace(qualified_import_name.getQualifier(), mNewPackageName, null); } } @@ -619,7 +619,7 @@ public class RenamePackageAction implements IObjectActionDelegate { IProject mProject; ApplicationPackageNameRefactoring(final IProject project) { - this.mProject = project; + mProject = project; } @Override @@ -636,7 +636,7 @@ public class RenamePackageAction implements IObjectActionDelegate { // Accurate refactoring of the "shorthand" names in // AndroidManifest.xml // depends on not having compilation errors. - if (this.mProject.findMaxProblemSeverity( + if (mProject.findMaxProblemSeverity( IMarker.PROBLEM, true, IResource.DEPTH_INFINITE) == IMarker.SEVERITY_ERROR) { @@ -653,7 +653,7 @@ public class RenamePackageAction implements IObjectActionDelegate { // Traverse all files in the project, building up a list of changes JavaFileVisitor file_visitor = new JavaFileVisitor(); - this.mProject.accept(file_visitor); + mProject.accept(file_visitor); return file_visitor.getChange(); } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ApkBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ApkBuilder.java index c3983b0..9cd3036 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ApkBuilder.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ApkBuilder.java @@ -213,7 +213,7 @@ public class ApkBuilder extends BaseBuilder { // get the list of referenced projects. javaProjects = ProjectHelper.getReferencedProjects(project); - IJavaProject[] referencedJavaProjects = getJavaProjects(javaProjects); + IJavaProject[] referencedJavaProjects = ApkBuilderHelper.getJavaProjects(javaProjects); // mix the java project and the library projects final int libCount = libProjects != null ? libProjects.length : 0; @@ -427,7 +427,8 @@ public class ApkBuilder extends BaseBuilder { removeMarkersFromContainer(project, AndroidConstants.MARKER_AAPT_PACKAGE); // need to figure out some path before we can execute aapt; - if (helper.packageResources( manifestFile, libProjects, osBinPath, + if (helper.packageResources( manifestFile, libProjects, null /*resfilter*/, + 0 /*versionCode */, osBinPath, AndroidConstants.FN_RESOURCES_AP_) == false) { // aapt failed. Whatever files that needed to be marked // have already been marked. We just return. @@ -477,9 +478,10 @@ public class ApkBuilder extends BaseBuilder { String classesDexPath = osBinPath + File.separator + AndroidConstants.FN_CLASSES_DEX; - if (helper.finalPackage(osBinPath + File.separator + AndroidConstants.FN_RESOURCES_AP_, - classesDexPath, osFinalPackagePath, javaProject, libProjects, - referencedJavaProjects, debuggable) == false) { + if (helper.finalPackage( + osBinPath + File.separator + AndroidConstants.FN_RESOURCES_AP_, + classesDexPath, osFinalPackagePath, javaProject, libProjects, + referencedJavaProjects, null /*abiFilter*/, debuggable) == false) { return allRefProjects; } @@ -537,25 +539,6 @@ public class ApkBuilder extends BaseBuilder { mBuildFinalPackage = loadProjectBooleanProperty(PROPERTY_BUILD_APK, true); } - /** - * Returns an array of {@link IJavaProject} matching the provided {@link IProject} objects. - * @param projects the IProject objects. - * @return an array, always. Can be empty. - * @throws CoreException - */ - private IJavaProject[] getJavaProjects(IProject[] projects) throws CoreException { - ArrayList<IJavaProject> list = new ArrayList<IJavaProject>(); - - for (IProject p : projects) { - if (p.isOpen() && p.hasNature(JavaCore.NATURE_ID)) { - - list.add(JavaCore.create(p)); - } - } - - return list.toArray(new IJavaProject[list.size()]); - } - @Override protected void abortOnBadSetup(IJavaProject javaProject) throws CoreException { super.abortOnBadSetup(javaProject); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ApkBuilderHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ApkBuilderHelper.java index fa5993e..954e2ed 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ApkBuilderHelper.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ApkBuilderHelper.java @@ -66,7 +66,7 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; -class ApkBuilderHelper { +public class ApkBuilderHelper { final static String GDBSERVER_NAME = "gdbserver"; //$NON-NLS-1$ @@ -121,15 +121,26 @@ class ApkBuilderHelper { private final JavaAndNativeResourceFilter mResourceFilter = new JavaAndNativeResourceFilter(); - ApkBuilderHelper(IProject project, PrintStream outStream, PrintStream errStream) { + public ApkBuilderHelper(IProject project, PrintStream outStream, PrintStream errStream) { mProject = project; mOutStream = outStream; mErrStream = errStream; } - - boolean packageResources(IFile manifestFile, IProject[] libProjects, - String outputFolder, String outputFilename) { + /** + * Packages the resources of the projet into a .ap_ file. + * @param manifestFile the manifest of the project. + * @param libProjects the list of library projects that this project depends on. + * @param resFilter an optional resource filter to be used with the -c option of aapt. If null + * no filters are used. + * @param versionCode an optional versionCode to be inserted in the manifest during packaging. + * If the value is <=0, no values are inserted. + * @param outputFolder where to write the resource ap_ file. + * @param outputFilename the name of the resource ap_ file. + * @return true if success. + */ + public boolean packageResources(IFile manifestFile, IProject[] libProjects, String resFilter, + int versionCode, String outputFolder, String outputFilename) { // need to figure out some path before we can execute aapt; // get the resource folder @@ -170,8 +181,8 @@ class ApkBuilderHelper { // build the default resource package if (executeAapt(osManifestPath, osResPaths, osAssetsPath, - outputFolder + File.separator + outputFilename, - null /*configFilter*/) == false) { + outputFolder + File.separator + outputFilename, resFilter, + versionCode) == false) { // aapt failed. Whatever files that needed to be marked // have already been marked. We just return. return false; @@ -191,13 +202,15 @@ class ApkBuilderHelper { * @param javaProject the java project being compiled * @param libProjects an optional list of library projects (can be null) * @param referencedJavaProjects referenced projects. + * @param abiFilter an optional filter. If not null, then only the matching ABI is included in + * the final archive * @param debuggable whether the project manifest has debuggable==true. If true, any gdbserver * executables will be packaged with the native libraries. * @return true if success, false otherwise. */ - boolean finalPackage(String intermediateApk, String dex, String output, + public boolean finalPackage(String intermediateApk, String dex, String output, final IJavaProject javaProject, IProject[] libProjects, - IJavaProject[] referencedJavaProjects, boolean debuggable) { + IJavaProject[] referencedJavaProjects, String abiFilter, boolean debuggable) { FileOutputStream fos = null; try { @@ -327,7 +340,7 @@ class ApkBuilderHelper { if (libFolder != null && libFolder.exists() && libFolder.getType() == IResource.FOLDER) { // look inside and put .so in lib/* by keeping the relative folder path. - writeNativeLibraries((IFolder) libFolder, builder, debuggable); + writeNativeLibraries((IFolder) libFolder, builder, abiFilter, debuggable); } // write the native libraries for the library projects. @@ -337,7 +350,7 @@ class ApkBuilderHelper { if (libFolder != null && libFolder.exists() && libFolder.getType() == IResource.FOLDER) { // look inside and put .so in lib/* by keeping the relative folder path. - writeNativeLibraries((IFolder) libFolder, builder, debuggable); + writeNativeLibraries((IFolder) libFolder, builder, abiFilter, debuggable); } } } @@ -505,11 +518,13 @@ class ApkBuilderHelper { * @param configFilter The configuration filter for the resources to include * (used with -c option, for example "port,en,fr" to include portrait, English and French * resources.) + * @param versionCode optional version code to insert in the manifest during packaging. If <=0 + * then no value is inserted * @return true if success, false otherwise. */ private boolean executeAapt(String osManifestPath, List<String> osResPaths, String osAssetsPath, String osOutFilePath, - String configFilter) { + String configFilter, int versionCode) { IAndroidTarget target = Sdk.getCurrent().getTarget(mProject); // Create the command line. @@ -527,6 +542,11 @@ class ApkBuilderHelper { commandArray.add("--auto-add-overlay"); //$NON-NLS-1$ } + if (versionCode > 0) { + commandArray.add("--version-code"); //$NON-NLS-1$ + commandArray.add(Integer.toString(versionCode)); + } + if (configFilter != null) { commandArray.add("-c"); //$NON-NLS-1$ commandArray.add(configFilter); @@ -631,19 +651,26 @@ class ApkBuilderHelper { * * @param rootFolder The folder containing the native libraries. * @param jarBuilder the {@link SignedJarBuilder} used to create the archive. + * @param abiFilter an optional filter. If not null, then only the matching ABI is included in + * the final archive * @param debuggable whether the application is debuggable. If <code>true</code> then gdbserver * executables will be packaged as well. * @throws CoreException * @throws IOException */ private void writeNativeLibraries(IFolder rootFolder, SignedJarBuilder jarBuilder, - boolean debuggable) - throws CoreException, IOException { + String abiFilter, boolean debuggable) throws CoreException, IOException { // the native files must be under a single sub-folder under the main root folder. // the sub-folder represents the abi for the native libs IResource[] abis = rootFolder.members(); for (IResource abi : abis) { if (abi.getType() == IResource.FOLDER) { // ignore non folders. + + // check the abi filter and reject all other ABIs + if (abiFilter != null && abiFilter.equals(abi.getName()) == false) { + continue; + } + IResource[] libs = ((IFolder)abi).members(); for (IResource lib : libs) { @@ -908,4 +935,24 @@ class ApkBuilderHelper { return JavaResourceFilter.checkFolderForPackaging(name); } + /** + * Returns an array of {@link IJavaProject} matching the provided {@link IProject} objects. + * @param projects the IProject objects. + * @return an array, always. Can be empty. + * @throws CoreException + */ + public static IJavaProject[] getJavaProjects(IProject[] projects) throws CoreException { + ArrayList<IJavaProject> list = new ArrayList<IJavaProject>(); + + for (IProject p : projects) { + if (p.isOpen() && p.hasNature(JavaCore.NATURE_ID)) { + + list.add(JavaCore.create(p)); + } + } + + return list.toArray(new IJavaProject[list.size()]); + } + + } diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/MultiApkExportHelper.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/MultiApkExportHelper.java index f365886..ee2a5a6 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/MultiApkExportHelper.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/MultiApkExportHelper.java @@ -33,6 +33,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; +import java.io.PrintStream; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; @@ -68,6 +69,7 @@ public class MultiApkExportHelper { private final static String FILE_CONFIG = "projects.config"; private final static String FILE_MINOR_CODE = "minor.codes"; private final static String FOLDER_LOG = "logs"; + private final PrintStream mStdio; public static final class ExportException extends Exception { private static final long serialVersionUID = 1L; @@ -115,11 +117,12 @@ public class MultiApkExportHelper { } public MultiApkExportHelper(String exportProjectRoot, String appPackage, - int versionCode, Target target) { + int versionCode, Target target, PrintStream stdio) { mExportProjectRoot = exportProjectRoot; mAppPackage = appPackage; mVersionCode = versionCode; mTarget = target; + mStdio = stdio; } public List<ApkData> getApkData(String projectList) throws ExportException { @@ -373,7 +376,7 @@ public class MultiApkExportHelper { } // output the relative path resolution. - System.out.println(String.format("%1$s => %2$s", relativePath, + mStdio.println(String.format("%1$s => %2$s", relativePath, projectFolder.getAbsolutePath())); // parse the manifest of the project. |