diff options
28 files changed, 909 insertions, 288 deletions
diff --git a/anttasks/src/com/android/ant/AndroidInitTask.java b/anttasks/src/com/android/ant/AndroidInitTask.java index 30779c5..d5bc55b 100644 --- a/anttasks/src/com/android/ant/AndroidInitTask.java +++ b/anttasks/src/com/android/ant/AndroidInitTask.java @@ -55,6 +55,12 @@ public class AndroidInitTask extends ImportTask { private final static String PROPERTY_ANDROID_JAR = "android-jar"; // ant property with the path to the framework.jar private final static String PROPERTY_ANDROID_AIDL = "android-aidl"; + // ant property with the path to the aapt tool + private final static String PROPERTY_AAPT = "aapt"; + // ant property with the path to the aidl tool + private final static String PROPERTY_AIDL = "aidl"; + // ant property with the path to the dx tool + private final static String PROPERTY_DX = "dx"; // ref id to the <path> object containing all the boot classpaths. private final static String REF_CLASSPATH = "android.target.classpath"; @@ -122,15 +128,19 @@ public class AndroidInitTask extends ImportTask { System.out.println("Project Target: " + androidTarget.getName()); if (androidTarget.isPlatform() == false) { System.out.println("Vendor: " + androidTarget.getVendor()); + System.out.println("Platform Version: " + androidTarget.getApiVersionName()); } - System.out.println("Platform Version: " + androidTarget.getApiVersionName()); System.out.println("API level: " + androidTarget.getApiVersionNumber()); - // sets up the properties to find android.jar/framework.aidl + // sets up the properties to find android.jar/framework.aidl/target tools String androidJar = androidTarget.getPath(IAndroidTarget.ANDROID_JAR); - String androidAidl = androidTarget.getPath(IAndroidTarget.ANDROID_AIDL); antProject.setProperty(PROPERTY_ANDROID_JAR, androidJar); - antProject.setProperty(PROPERTY_ANDROID_AIDL, androidAidl); + + antProject.setProperty(PROPERTY_ANDROID_AIDL, + androidTarget.getPath(IAndroidTarget.ANDROID_AIDL)); + antProject.setProperty(PROPERTY_AAPT, androidTarget.getPath(IAndroidTarget.AAPT)); + antProject.setProperty(PROPERTY_AIDL, androidTarget.getPath(IAndroidTarget.AIDL)); + antProject.setProperty(PROPERTY_DX, androidTarget.getPath(IAndroidTarget.DX)); // sets up the boot classpath diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/EmulatorConsole.java b/ddms/libs/ddmlib/src/com/android/ddmlib/EmulatorConsole.java index 74c432d..f3986ed 100644 --- a/ddms/libs/ddmlib/src/com/android/ddmlib/EmulatorConsole.java +++ b/ddms/libs/ddmlib/src/com/android/ddmlib/EmulatorConsole.java @@ -54,7 +54,7 @@ public final class EmulatorConsole { private final static String HOST = "127.0.0.1"; //$NON-NLS-1$ private final static String COMMAND_PING = "help\r\n"; //$NON-NLS-1$ - private final static String COMMAND_AVD_NAME = "vm name\r\n"; //$NON-NLS-1$ // TODO change with emulator + private final static String COMMAND_AVD_NAME = "avd name\r\n"; //$NON-NLS-1$ private final static String COMMAND_KILL = "kill\r\n"; //$NON-NLS-1$ private final static String COMMAND_GSM_STATUS = "gsm status\r\n"; //$NON-NLS-1$ private final static String COMMAND_GSM_CALL = "gsm call %1$s\r\n"; //$NON-NLS-1$ diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/DevicePanel.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/DevicePanel.java index d168476..81b757e 100644 --- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/DevicePanel.java +++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/DevicePanel.java @@ -205,6 +205,10 @@ public final class DevicePanel extends Panel implements IDebugBridgeChangeListen String debuggable = device.getProperty(Device.PROP_DEBUGGABLE); if (device.isEmulator()) { String avdName = device.getAvdName(); + if (avdName == null) { + avdName = "?"; // the device is probably not online yet, so + // we don't know its AVD name just yet. + } if (debuggable != null && debuggable.equals("1")) { //$NON-NLS-1$ return String.format("%1$s [%2$s, debug]", avdName, version); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java index ddc93ac..9aa9354 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java @@ -20,7 +20,6 @@ import com.android.ddmuilib.StackTracePanel; import com.android.ddmuilib.StackTracePanel.ISourceRevealer; import com.android.ddmuilib.console.DdmConsole; import com.android.ddmuilib.console.IDdmConsole; -import com.android.ide.eclipse.adt.build.DexWrapper; import com.android.ide.eclipse.adt.debug.launching.AndroidLaunchController; import com.android.ide.eclipse.adt.preferences.BuildPreferencePage; import com.android.ide.eclipse.adt.project.ProjectHelper; @@ -423,8 +422,6 @@ public class AdtPlugin extends AbstractUIPlugin { stopEditors(); - DexWrapper.unloadDex(); - mRed.dispose(); synchronized (AdtPlugin.class) { sPlugin = null; @@ -465,21 +462,11 @@ public class AdtPlugin extends AbstractUIPlugin { return SdkConstants.OS_SDK_TOOLS_FOLDER + AndroidConstants.FN_ADB; } - /** Returns the aapt path relative to the sdk folder */ - public static String getOsRelativeAapt() { - return SdkConstants.OS_SDK_TOOLS_FOLDER + AndroidConstants.FN_AAPT; - } - /** Returns the emulator path relative to the sdk folder */ public static String getOsRelativeEmulator() { return SdkConstants.OS_SDK_TOOLS_FOLDER + AndroidConstants.FN_EMULATOR; } - /** Returns the aidl path relative to the sdk folder */ - public static String getOsRelativeAidl() { - return SdkConstants.OS_SDK_TOOLS_FOLDER + AndroidConstants.FN_AIDL; - } - /** Returns the absolute adb path */ public static String getOsAbsoluteAdb() { return getOsSdkFolder() + getOsRelativeAdb(); @@ -491,21 +478,11 @@ public class AdtPlugin extends AbstractUIPlugin { AndroidConstants.FN_TRACEVIEW; } - /** Returns the absolute aapt path */ - public static String getOsAbsoluteAapt() { - return getOsSdkFolder() + getOsRelativeAapt(); - } - /** Returns the absolute emulator path */ public static String getOsAbsoluteEmulator() { return getOsSdkFolder() + getOsRelativeEmulator(); } - /** Returns the absolute aidl path */ - public static String getOsAbsoluteAidl() { - return getOsSdkFolder() + getOsRelativeAidl(); - } - /** * Returns a Url file path to the javaDoc folder. */ @@ -968,8 +945,6 @@ public class AdtPlugin extends AbstractUIPlugin { // check the path to various tools we use String[] filesToCheck = new String[] { osSdkLocation + getOsRelativeAdb(), - osSdkLocation + getOsRelativeAapt(), - osSdkLocation + getOsRelativeAidl(), osSdkLocation + getOsRelativeEmulator() }; for (String file : filesToCheck) { @@ -1058,17 +1033,6 @@ public class AdtPlugin extends AbstractUIPlugin { // FIXME: move this per platform, or somewhere else. progress = SubMonitor.convert(monitor, Messages.AdtPlugin_Parsing_Resources, 20); - DexWrapper.unloadDex(); - - IStatus res = DexWrapper.loadDex( - mOsSdkLocation + AndroidConstants.OS_SDK_LIBS_DX_JAR); - if (res != Status.OK_STATUS) { - synchronized (getSdkLockObject()) { - mSdkIsLoaded = LoadStatus.FAILED; - mPostLoadProjectsToResolve.clear(); - } - return res; - } synchronized (getSdkLockObject()) { mSdkIsLoaded = LoadStatus.LOADED; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java index 43971b0..c359905 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java @@ -19,7 +19,7 @@ package com.android.ide.eclipse.adt.build; import com.android.ide.eclipse.adt.AdtConstants; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.project.ProjectHelper; -import com.android.ide.eclipse.adt.sdk.LoadStatus; +import com.android.ide.eclipse.adt.sdk.AndroidTargetData; import com.android.ide.eclipse.adt.sdk.Sdk; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.project.BaseProjectHelper; @@ -67,6 +67,8 @@ import java.text.DateFormat; import java.util.ArrayList; import java.util.Date; import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; public class ApkBuilder extends BaseBuilder { @@ -280,8 +282,12 @@ public class ApkBuilder extends BaseBuilder { } } - // also check the final file! - String finalPackageName = project.getName() + AndroidConstants.DOT_ANDROID_PACKAGE; + // get the extra configs for the project. This will give us a list of custom apk + // to build based on a restricted set of resources. + Map<String, String> configs = Sdk.getCurrent().getProjectConfigs(project); + + // also check the final file(s)! + String finalPackageName = getFileName(project, null /*config*/); if (mBuildFinalPackage == false && outputFolder != null) { tmp = outputFolder.findMember(finalPackageName); if (tmp == null || (tmp instanceof IFile && @@ -290,6 +296,24 @@ public class ApkBuilder extends BaseBuilder { AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg); mBuildFinalPackage = true; } + + if (configs != null) { + Set<Entry<String, String>> entrySet = configs.entrySet(); + + for (Entry<String, String> entry : entrySet) { + String filename = getFileName(project, entry.getKey()); + + tmp = outputFolder.findMember(filename); + if (tmp == null || (tmp instanceof IFile && + tmp.exists() == false)) { + String msg = String.format(Messages.s_Missing_Repackaging, + finalPackageName); + AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg); + mBuildFinalPackage = true; + break; + } + } + } } // store the build status in the persistent storage @@ -358,6 +382,17 @@ public class ApkBuilder extends BaseBuilder { // handle already present .apk, and if that one failed as well, the user will be // notified. finalPackage.delete(); + + if (configs != null) { + Set<Entry<String, String>> entrySet = configs.entrySet(); + for (Entry<String, String> entry : entrySet) { + String packageFilepath = osBinPath + File.separator + + getFileName(project, entry.getKey()); + + finalPackage = new File(packageFilepath); + finalPackage.delete(); + } + } // first we check if we need to package the resources. if (mPackageResources) { @@ -401,13 +436,30 @@ public class ApkBuilder extends BaseBuilder { osAssetsPath = assetsFolder.getLocation().toOSString(); } + // build the default resource package if (executeAapt(project, osManifestPath, osResPath, osAssetsPath, osBinPath + File.separator + - AndroidConstants.FN_RESOURCES_AP_) == false) { + AndroidConstants.FN_RESOURCES_AP_, null /*configFilter*/) == false) { // aapt failed. Whatever files that needed to be marked // have already been marked. We just return. return referencedProjects; } + + // now do the same thing for all the configured resource packages. + if (configs != null) { + Set<Entry<String, String>> entrySet = configs.entrySet(); + for (Entry<String, String> entry : entrySet) { + String outPathFormat = osBinPath + File.separator + + AndroidConstants.FN_RESOURCES_S_AP_; + String outPath = String.format(outPathFormat, entry.getKey()); + if (executeAapt(project, osManifestPath, osResPath, + osAssetsPath, outPath, entry.getValue()) == false) { + // aapt failed. Whatever files that needed to be marked + // have already been marked. We just return. + return referencedProjects; + } + } + } // build has been done. reset the state of the builder mPackageResources = false; @@ -433,25 +485,49 @@ public class ApkBuilder extends BaseBuilder { } // now we need to make the final package from the intermediary apk - // and classes.dex + // and classes.dex. + // This is the default package with all the resources. + String classesDexPath = osBinPath + File.separator + AndroidConstants.FN_CLASSES_DEX; if (finalPackage(osBinPath + File.separator + AndroidConstants.FN_RESOURCES_AP_, - osBinPath + File.separator + AndroidConstants.FN_CLASSES_DEX, - osFinalPackagePath, javaProject, referencedJavaProjects) == false) { + classesDexPath,osFinalPackagePath, javaProject, + referencedJavaProjects) == false) { return referencedProjects; - } else { - // get the resource to bin - outputFolder.refreshLocal(IResource.DEPTH_ONE, monitor); + } + + // now do the same thing for all the configured resource packages. + if (configs != null) { + String resPathFormat = osBinPath + File.separator + + AndroidConstants.FN_RESOURCES_S_AP_; + + Set<Entry<String, String>> entrySet = configs.entrySet(); + for (Entry<String, String> entry : entrySet) { + // make the filename for the resource package. + String resPath = String.format(resPathFormat, entry.getKey()); + + // make the filename for the apk to generate + String apkOsFilePath = osBinPath + File.separator + + getFileName(project, entry.getKey()); + if (finalPackage(resPath, classesDexPath, apkOsFilePath, javaProject, + referencedJavaProjects) == false) { + return referencedProjects; + } + } + } - // build has been done. reset the state of the builder - mBuildFinalPackage = false; + // we are done. + + // get the resource to bin + outputFolder.refreshLocal(IResource.DEPTH_ONE, monitor); - // and store it - saveProjectBooleanProperty(PROPERTY_BUILD_APK, mBuildFinalPackage); - - AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, getProject(), - "Build Success!"); - } + // build has been done. reset the state of the builder + mBuildFinalPackage = false; + + // and store it + saveProjectBooleanProperty(PROPERTY_BUILD_APK, mBuildFinalPackage); + + AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, getProject(), + "Build Success!"); } return referencedProjects; } @@ -475,19 +551,26 @@ public class ApkBuilder extends BaseBuilder { * @param osResPath The path to the res folder * @param osAssetsPath The path to the assets folder. This can be null. * @param osOutFilePath The path to the temporary resource file to create. + * @param configFilter The configuration filter for the resources to include + * (used with -c option) * @return true if success, false otherwise. */ private boolean executeAapt(IProject project, String osManifestPath, - String osResPath, String osAssetsPath, String osOutFilePath) { + String osResPath, String osAssetsPath, String osOutFilePath, String configFilter) { + IAndroidTarget target = Sdk.getCurrent().getTarget(project); // Create the command line. ArrayList<String> commandArray = new ArrayList<String>(); - commandArray.add(AdtPlugin.getOsAbsoluteAapt()); + commandArray.add(target.getPath(IAndroidTarget.AAPT)); commandArray.add("package"); //$NON-NLS-1$ commandArray.add("-f");//$NON-NLS-1$ if (AdtPlugin.getBuildVerbosity() == AdtConstants.BUILD_VERBOSE) { commandArray.add("-v"); //$NON-NLS-1$ } + if (configFilter != null) { + commandArray.add("-c"); //$NON-NLS-1$ + commandArray.add(configFilter); + } commandArray.add("-M"); //$NON-NLS-1$ commandArray.add(osManifestPath); commandArray.add("-S"); //$NON-NLS-1$ @@ -497,8 +580,7 @@ public class ApkBuilder extends BaseBuilder { commandArray.add(osAssetsPath); } commandArray.add("-I"); //$NON-NLS-1$ - commandArray.add( - Sdk.getCurrent().getTarget(project).getPath(IAndroidTarget.ANDROID_JAR)); + commandArray.add(target.getPath(IAndroidTarget.ANDROID_JAR)); commandArray.add("-F"); //$NON-NLS-1$ commandArray.add(osOutFilePath); @@ -576,14 +658,19 @@ public class ApkBuilder extends BaseBuilder { */ private boolean executeDx(IJavaProject javaProject, String osBinPath, String osOutFilePath, IJavaProject[] referencedJavaProjects) throws CoreException { + IAndroidTarget target = Sdk.getCurrent().getTarget(javaProject.getProject()); + AndroidTargetData targetData = Sdk.getCurrent().getTargetData(target); + if (targetData == null) { + throw new CoreException(new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, + Messages.ApkBuilder_UnableBuild_Dex_Not_loaded)); + } + // get the dex wrapper - DexWrapper wrapper = DexWrapper.getWrapper(); + DexWrapper wrapper = targetData.getDexWrapper(); if (wrapper == null) { - if (DexWrapper.getStatus() == LoadStatus.FAILED) { - throw new CoreException(new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, - Messages.ApkBuilder_UnableBuild_Dex_Not_loaded)); - } + throw new CoreException(new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, + Messages.ApkBuilder_UnableBuild_Dex_Not_loaded)); } // make sure dx use the proper output streams. @@ -1003,6 +1090,19 @@ public class ApkBuilder extends BaseBuilder { return list.toArray(new IJavaProject[list.size()]); } + + /** + * Returns the apk filename for the given project + * @param project The project. + * @param config An optional config name. Can be null. + */ + private static String getFileName(IProject project, String config) { + if (config != null) { + return project.getName() + "-" + config + AndroidConstants.DOT_ANDROID_PACKAGE; //$NON-NLS-1$ + } + + return project.getName() + AndroidConstants.DOT_ANDROID_PACKAGE; + } /** * Checks a {@link IFile} to make sure it should be packaged as standard resources. diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkDeltaVisitor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkDeltaVisitor.java index aec703d..5d6793a 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkDeltaVisitor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkDeltaVisitor.java @@ -192,11 +192,16 @@ public class ApkDeltaVisitor extends BaseDeltaVisitor IPath parentPath = path.removeLastSegments(1); if (mOutputPath.equals(parentPath)) { String resourceName = resource.getName(); + // check if classes.dex was removed if (resourceName.equalsIgnoreCase(AndroidConstants.FN_CLASSES_DEX)) { mConvertToDex = true; mMakeFinalPackage = true; } else if (resourceName.equalsIgnoreCase( - AndroidConstants.FN_RESOURCES_AP_)) { + AndroidConstants.FN_RESOURCES_AP_) || + AndroidConstants.PATTERN_RESOURCES_S_AP_.matcher( + resourceName).matches()) { + // or if the default resources.ap_ or a configured version + // (resources-###.ap_) was removed. mPackageResources = true; mMakeFinalPackage = true; } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/DexWrapper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/DexWrapper.java index cba8ad7..26d96d7 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/DexWrapper.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/DexWrapper.java @@ -17,7 +17,6 @@ package com.android.ide.eclipse.adt.build; import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.sdk.LoadStatus; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; @@ -46,10 +45,6 @@ public final class DexWrapper { private final static String MAIN_RUN = "run"; //$NON-NLS-1$ - private static DexWrapper sWrapper; - - private static LoadStatus sLoadStatus = LoadStatus.LOADING; - private Method mRunMethod; private Constructor<?> mArgConstructor; @@ -67,10 +62,8 @@ public final class DexWrapper { * @param osFilepath the location of the dex.jar file. * @return an IStatus indicating the result of the load. */ - public static synchronized IStatus loadDex(String osFilepath) { + public synchronized IStatus loadDex(String osFilepath) { try { - sWrapper = null; - File f = new File(osFilepath); if (f.isFile() == false) { return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, String.format( @@ -86,45 +79,39 @@ public final class DexWrapper { Class<?> consoleClass = loader.loadClass(DEX_CONSOLE); Class<?> argClass = loader.loadClass(DEX_ARGS); - sWrapper = new DexWrapper(mainClass, argClass, consoleClass); - + try { + // now get the fields/methods we need + mRunMethod = mainClass.getMethod(MAIN_RUN, argClass); + + mArgConstructor = argClass.getConstructor(); + mArgOutName = argClass.getField("outName"); //$NON-NLS-1$ + mArgJarOutput = argClass.getField("jarOutput"); //$NON-NLS-1$ + mArgFileNames = argClass.getField("fileNames"); //$NON-NLS-1$ + mArgVerbose = argClass.getField("verbose"); //$NON-NLS-1$ + + mConsoleOut = consoleClass.getField("out"); //$NON-NLS-1$ + mConsoleErr = consoleClass.getField("err"); //$NON-NLS-1$ + + } catch (SecurityException e) { + return createErrorStatus(Messages.DexWrapper_SecuryEx_Unable_To_Find_API, e); + } catch (NoSuchMethodException e) { + return createErrorStatus(Messages.DexWrapper_SecuryEx_Unable_To_Find_Method, e); + } catch (NoSuchFieldException e) { + return createErrorStatus(Messages.DexWrapper_SecuryEx_Unable_To_Find_Field, e); + } + return Status.OK_STATUS; } catch (MalformedURLException e) { // really this should not happen. - return createErrorStatus(String.format(Messages.DexWrapper_Failed_to_load_s, osFilepath), e); + return createErrorStatus( + String.format(Messages.DexWrapper_Failed_to_load_s, osFilepath), e); } catch (ClassNotFoundException e) { - return createErrorStatus(String.format(Messages.DexWrapper_Failed_to_load_s, osFilepath), e); - } catch (CoreException e) { - return e.getStatus(); - } finally { - if (sWrapper == null) { - sLoadStatus = LoadStatus.FAILED; - } else { - sLoadStatus = LoadStatus.LOADED; - } + return createErrorStatus( + String.format(Messages.DexWrapper_Failed_to_load_s, osFilepath), e); } } /** - * Unloads the loaded dex wrapper. - */ - public static synchronized void unloadDex() { - sWrapper = null; - sLoadStatus = LoadStatus.LOADING; - } - - public static synchronized DexWrapper getWrapper() { - return sWrapper; - } - - /** - * Returns the {@link LoadStatus}. - */ - public static synchronized LoadStatus getStatus() { - return sLoadStatus; - } - - /** * Runs the dex command. * @param osOutFilePath the OS path to the outputfile (classes.dex * @param osFilenames list of input source files (.class and .jar files) @@ -169,33 +156,6 @@ public final class DexWrapper { } } - private DexWrapper(Class<?> mainClass, Class<?> argClass, Class<?> consoleClass) - throws CoreException { - try { - // now get the fields/methods we need - mRunMethod = mainClass.getMethod(MAIN_RUN, argClass); - - mArgConstructor = argClass.getConstructor(); - mArgOutName = argClass.getField("outName"); //$NON-NLS-1$ - mArgJarOutput = argClass.getField("jarOutput"); //$NON-NLS-1$ - mArgFileNames = argClass.getField("fileNames"); //$NON-NLS-1$ - mArgVerbose = argClass.getField("verbose"); //$NON-NLS-1$ - - mConsoleOut = consoleClass.getField("out"); //$NON-NLS-1$ - mConsoleErr = consoleClass.getField("err"); //$NON-NLS-1$ - - } catch (SecurityException e) { - throw new CoreException(createErrorStatus( - Messages.DexWrapper_SecuryEx_Unable_To_Find_API, e)); - } catch (NoSuchMethodException e) { - throw new CoreException(createErrorStatus( - Messages.DexWrapper_SecuryEx_Unable_To_Find_Method, e)); - } catch (NoSuchFieldException e) { - throw new CoreException(createErrorStatus( - Messages.DexWrapper_SecuryEx_Unable_To_Find_Field, e)); - } - } - private static IStatus createErrorStatus(String message, Exception e) { AdtPlugin.log(e, message); AdtPlugin.printErrorToConsole(Messages.DexWrapper_Dex_Loader, message); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java index 2c15d55..958cac2 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java @@ -235,6 +235,7 @@ public class PreCompilerBuilder extends BaseBuilder { // get the project objects IProject project = getProject(); IJavaProject javaProject = JavaCore.create(project); + IAndroidTarget projectTarget = Sdk.getCurrent().getTarget(project); // now we need to get the classpath list ArrayList<IPath> sourceList = BaseProjectHelper.getSourceClasspaths(javaProject); @@ -407,8 +408,6 @@ public class PreCompilerBuilder extends BaseBuilder { String osResPath = resLocation.toOSString(); String osManifestPath = manifestLocation.toOSString(); - IAndroidTarget projectTarget = Sdk.getCurrent().getTarget(project); - // remove the aapt markers removeMarkersFromFile(manifest, AndroidConstants.MARKER_AAPT); removeMarkersFromContainer(resFolder, AndroidConstants.MARKER_AAPT); @@ -432,7 +431,7 @@ public class PreCompilerBuilder extends BaseBuilder { // launch aapt: create the command line ArrayList<String> array = new ArrayList<String>(); - array.add(AdtPlugin.getOsAbsoluteAapt()); + array.add(projectTarget.getPath(IAndroidTarget.AAPT)); array.add("package"); //$NON-NLS-1$ array.add("-m"); //$NON-NLS-1$ if (AdtPlugin.getBuildVerbosity() == AdtConstants.BUILD_VERBOSE) { @@ -541,7 +540,7 @@ public class PreCompilerBuilder extends BaseBuilder { if (projectAidl != null && projectAidl.exists()) { folderAidlPath = projectAidl.getLocation().toOSString(); } - boolean aidlStatus = handleAidl(sourceList, folderAidlPath, monitor); + boolean aidlStatus = handleAidl(projectTarget, sourceList, folderAidlPath, monitor); if (aidlStatus == false && mCompileResources == false) { AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, @@ -729,14 +728,15 @@ public class PreCompilerBuilder extends BaseBuilder { /** * Compiles aidl files into java. This will also removes old java files * created from aidl files that are now gone. + * @param projectTarget Target of the project * @param sourceFolders the list of source folders, relative to the workspace. * @param folderAidlPath * @param monitor the projess monitor * @returns true if it did something * @throws CoreException */ - private boolean handleAidl(ArrayList<IPath> sourceFolders, String folderAidlPath, - IProgressMonitor monitor) throws CoreException { + private boolean handleAidl(IAndroidTarget projectTarget, ArrayList<IPath> sourceFolders, + String folderAidlPath, IProgressMonitor monitor) throws CoreException { if (mAidlToCompile.size() == 0 && mAidlToRemove.size() == 0) { return false; } @@ -746,7 +746,7 @@ public class PreCompilerBuilder extends BaseBuilder { String[] command = new String[4 + sourceFolders.size() + (folderAidlPath != null ? 1 : 0)]; int index = 0; int aidlIndex; - command[index++] = AdtPlugin.getOsAbsoluteAidl(); + command[index++] = projectTarget.getPath(IAndroidTarget.AIDL); command[aidlIndex = index++] = "-p"; //$NON-NLS-1$ if (folderAidlPath != null) { command[index++] = "-p" + folderAidlPath; //$NON-NLS-1$ diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/AndroidLaunchController.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/AndroidLaunchController.java index d4952b1..ac003df 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/AndroidLaunchController.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/AndroidLaunchController.java @@ -1519,6 +1519,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener AdtPlugin.printErrorToConsole(launchInfo.mProject, String.format(message1, launchInfo.mActivity)); } + launchInfo.mLaunch.stopLaunch(); } for (DelayedLaunchInfo launchInfo : mWaitingForDebuggerApplications) { if (launchInfo.mLaunchAction == LaunchConfigDelegate.ACTION_DO_NOTHING) { @@ -1527,7 +1528,11 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener AdtPlugin.printErrorToConsole(launchInfo.mProject, String.format(message1, launchInfo.mActivity)); } + launchInfo.mLaunch.stopLaunch(); } + + mWaitingForReadyEmulatorList.clear(); + mWaitingForDebuggerApplications.clear(); } } @@ -1573,24 +1578,31 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener * * @see IDeviceChangeListener#deviceDisconnected(Device) */ + @SuppressWarnings("unchecked") public void deviceDisconnected(Device device) { // any pending launch on this device must be canceled. String message = "%1$s disconnected! Cancelling '%2$s' launch!"; synchronized (sListLock) { - for (DelayedLaunchInfo launchInfo : mWaitingForReadyEmulatorList) { + ArrayList<DelayedLaunchInfo> copyList = + (ArrayList<DelayedLaunchInfo>)mWaitingForReadyEmulatorList.clone(); + for (DelayedLaunchInfo launchInfo : copyList) { if (launchInfo.mDevice == device) { AdtPlugin.printErrorToConsole(launchInfo.mProject, String.format(message, device.getSerialNumber(), launchInfo.mActivity)); + launchInfo.mLaunch.stopLaunch(); + mWaitingForReadyEmulatorList.remove(launchInfo); } } - for (DelayedLaunchInfo launchInfo : mWaitingForDebuggerApplications) { + copyList = (ArrayList<DelayedLaunchInfo>)mWaitingForDebuggerApplications.clone(); + for (DelayedLaunchInfo launchInfo : copyList) { if (launchInfo.mDevice == device) { AdtPlugin.printErrorToConsole(launchInfo.mProject, String.format(message, device.getSerialNumber(), launchInfo.mActivity)); + launchInfo.mLaunch.stopLaunch(); + mWaitingForDebuggerApplications.remove(launchInfo); } } } - } /** diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/DeviceChooserDialog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/DeviceChooserDialog.java index d446e2b..a260350 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/DeviceChooserDialog.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/DeviceChooserDialog.java @@ -310,7 +310,7 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener top.setLayout(new GridLayout(1, true)); mDeviceRadioButton = new Button(top, SWT.RADIO); - mDeviceRadioButton.setText("Choose an Android running device"); + mDeviceRadioButton.setText("Choose a running Android device"); mDeviceRadioButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { @@ -387,7 +387,7 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener }); Button radio2 = new Button(top, SWT.RADIO); - radio2.setText("Launch a new Virtual Device"); + radio2.setText("Launch a new Android Virtual Device"); // offset the selector from the radio button offsetComp = new Composite(top, SWT.NONE); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainerInitializer.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainerInitializer.java index 30bf7ed..339dcd0 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainerInitializer.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainerInitializer.java @@ -172,13 +172,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit // if we are loaded and the target is non null, we create a valid ClassPathContainer if (sdkIsLoaded && target != null) { - String targetName = null; - if (target.isPlatform()) { - targetName = target.getName(); - } else { - targetName = String.format("%1$s (%2$s)", target.getName(), - target.getApiVersionName()); - } + String targetName = target.getFullName(); return new AndroidClasspathContainer( createClasspathEntries(iProject, target, targetName), diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetData.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetData.java index 60561ab..2309181 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetData.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetData.java @@ -16,6 +16,7 @@ package com.android.ide.eclipse.adt.sdk; +import com.android.ide.eclipse.adt.build.DexWrapper; import com.android.ide.eclipse.common.resources.IResourceRepository; import com.android.ide.eclipse.editors.descriptors.IDescriptorProvider; import com.android.ide.eclipse.editors.layout.descriptors.LayoutDescriptors; @@ -54,6 +55,8 @@ public class AndroidTargetData { private final IAndroidTarget mTarget; + private DexWrapper mDexWrapper; + /** * mAttributeValues is a map { key => list [ values ] }. * The key for the map is "(element-xml-name,attribute-namespace:attribute-xml-local-name)". @@ -64,27 +67,34 @@ public class AndroidTargetData { * This is used for attributes that do not have a unique name, but still need to be populated * with values in the UI. Uniquely named attributes have their values in {@link #mEnumValueMap}. */ - private final Hashtable<String, String[]> mAttributeValues = new Hashtable<String, String[]>(); + private Hashtable<String, String[]> mAttributeValues = new Hashtable<String, String[]>(); private IResourceRepository mSystemResourceRepository; - private final AndroidManifestDescriptors mManifestDescriptors; - private final LayoutDescriptors mLayoutDescriptors; - private final MenuDescriptors mMenuDescriptors; - private final XmlDescriptors mXmlDescriptors; + private AndroidManifestDescriptors mManifestDescriptors; + private LayoutDescriptors mLayoutDescriptors; + private MenuDescriptors mMenuDescriptors; + private XmlDescriptors mXmlDescriptors; - private final Map<String, Map<String, Integer>> mEnumValueMap; + private Map<String, Map<String, Integer>> mEnumValueMap; - private final ProjectResources mFrameworkResources; - private final LayoutBridge mLayoutBridge; + private ProjectResources mFrameworkResources; + private LayoutBridge mLayoutBridge; private boolean mLayoutBridgeInit = false; + AndroidTargetData(IAndroidTarget androidTarget) { + mTarget = androidTarget; + } + + void setDexWrapper(DexWrapper wrapper) { + mDexWrapper = wrapper; + } + /** * Creates an AndroidTargetData object. */ - AndroidTargetData(IAndroidTarget androidTarget, - IResourceRepository systemResourceRepository, + void setExtraData(IResourceRepository systemResourceRepository, AndroidManifestDescriptors manifestDescriptors, LayoutDescriptors layoutDescriptors, MenuDescriptors menuDescriptors, @@ -98,7 +108,6 @@ public class AndroidTargetData { ProjectResources resources, LayoutBridge layoutBridge) { - mTarget = androidTarget; mSystemResourceRepository = systemResourceRepository; mManifestDescriptors = manifestDescriptors; mLayoutDescriptors = layoutDescriptors; @@ -113,6 +122,10 @@ public class AndroidTargetData { serviceIntentActionValues, intentCategoryValues); } + public DexWrapper getDexWrapper() { + return mDexWrapper; + } + public IResourceRepository getSystemResources() { return mSystemResourceRepository; } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetParser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetParser.java index dfe876f..aab660d 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetParser.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetParser.java @@ -17,6 +17,7 @@ package com.android.ide.eclipse.adt.sdk; import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.build.DexWrapper; import com.android.ide.eclipse.adt.sdk.AndroidTargetData.LayoutBridge; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.resources.AttrsXmlParser; @@ -91,8 +92,25 @@ public final class AndroidTargetParser { try { SubMonitor progress = SubMonitor.convert(monitor, String.format("Parsing SDK %1$s", mAndroidTarget.getName()), - 120); + 200); + AndroidTargetData targetData = new AndroidTargetData(mAndroidTarget); + + // load DX. + DexWrapper dexWrapper = new DexWrapper(); + IStatus res = dexWrapper.loadDex(mAndroidTarget.getPath(IAndroidTarget.DX_JAR)); + if (res != Status.OK_STATUS) { + return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, + String.format("dx.jar loading failed for target '%1$s'", + mAndroidTarget.getFullName())); + } + + // we have loaded dx. + targetData.setDexWrapper(dexWrapper); + + // parse the rest of the data. + progress.setWorkRemaining(120); + AndroidJarLoader classLoader = new AndroidJarLoader(mAndroidTarget.getPath(IAndroidTarget.ANDROID_JAR)); @@ -229,8 +247,7 @@ public final class AndroidTargetParser { progress.worked(10); // and finally create the PlatformData with all that we loaded. - AndroidTargetData targetData = new AndroidTargetData(mAndroidTarget, - frameworkRepository, + targetData.setExtraData(frameworkRepository, manifestDescriptors, layoutDescriptors, menuDescriptors, diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java index 01b722f..5bfa8a2 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java @@ -18,6 +18,8 @@ package com.android.ide.eclipse.adt.sdk; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.project.internal.AndroidClasspathContainerInitializer; +import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor; +import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IProjectListener; import com.android.prefs.AndroidLocation.AndroidLocationException; import com.android.sdklib.IAndroidTarget; import com.android.sdklib.ISdkLog; @@ -28,6 +30,8 @@ import com.android.sdklib.project.ProjectProperties; import com.android.sdklib.project.ProjectProperties.PropertyType; import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IncrementalProjectBuilder; +import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; @@ -38,6 +42,9 @@ import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; /** * Central point to load, manipulate and deal with the Android SDK. Only one SDK can be used @@ -48,18 +55,19 @@ import java.util.HashMap; * * To get the list of platforms or add-ons present in the SDK, call {@link #getTargets()}. */ -public class Sdk { +public class Sdk implements IProjectListener { private static Sdk sCurrentSdk = null; private final SdkManager mManager; private final AvdManager mAvdManager; - private final HashMap<IProject, IAndroidTarget> mProjectMap = + private final HashMap<IProject, IAndroidTarget> mProjectTargetMap = new HashMap<IProject, IAndroidTarget>(); - private final HashMap<IAndroidTarget, AndroidTargetData> mTargetMap = + private final HashMap<IAndroidTarget, AndroidTargetData> mTargetDataMap = new HashMap<IAndroidTarget, AndroidTargetData>(); + private final HashMap<IProject, Map<String, String>> mProjectConfigMap = + new HashMap<IProject, Map<String, String>>(); private final String mDocBaseUrl; - /** * Loads an SDK and returns an {@link Sdk} object if success. @@ -67,7 +75,7 @@ public class Sdk { */ public static Sdk loadSdk(String sdkLocation) { if (sCurrentSdk != null) { - // manual unload? + sCurrentSdk.dispose(); sCurrentSdk = null; } @@ -159,16 +167,16 @@ public class Sdk { * Associates an {@link IProject} and an {@link IAndroidTarget}. */ public void setProject(IProject project, IAndroidTarget target) { - synchronized (mProjectMap) { + synchronized (mProjectTargetMap) { // look for the current target of the project - IAndroidTarget previousTarget = mProjectMap.get(project); + IAndroidTarget previousTarget = mProjectTargetMap.get(project); if (target != previousTarget) { // save the target hash string in the project persistent property setProjectTargetHashString(project, target.hashString()); // put it in a local map for easy access. - mProjectMap.put(project, target); + mProjectTargetMap.put(project, target); // recompile the project if needed. IJavaProject javaProject = JavaCore.create(project); @@ -182,11 +190,11 @@ public class Sdk { * Returns the {@link IAndroidTarget} object associated with the given {@link IProject}. */ public IAndroidTarget getTarget(IProject project) { - synchronized (mProjectMap) { - IAndroidTarget target = mProjectMap.get(project); + synchronized (mProjectTargetMap) { + IAndroidTarget target = mProjectTargetMap.get(project); if (target == null) { // get the value from the project persistent property. - String targetHashString = getProjectTargetHashString(project); + String targetHashString = loadProjectProperties(project, this); if (targetHashString != null) { target = mManager.getTargetFromHashString(targetHashString); @@ -197,14 +205,19 @@ public class Sdk { } } + /** - * Returns the hash string uniquely identifying the target of a project. This methods reads - * the string from the project persistent preferences/properties. - * <p/>The string is equivalent to the return of {@link IAndroidTarget#hashString()}. + * Parses the project properties and returns the hash string uniquely identifying the + * target of the given project. + * <p/> + * This methods reads the content of the <code>default.properties</code> file present in + * the root folder of the project. + * <p/>The returned string is equivalent to the return of {@link IAndroidTarget#hashString()}. * @param project The project for which to return the target hash string. + * @param storeConfigs Whether the read configuration should be stored in the map. * @return the hash string or null if the project does not have a target set. */ - public static String getProjectTargetHashString(IProject project) { + private static String loadProjectProperties(IProject project, Sdk storeConfigs) { // load the default.properties from the project folder. ProjectProperties properties = ProjectProperties.load(project.getLocation().toOSString(), PropertyType.DEFAULT); @@ -214,11 +227,46 @@ public class Sdk { return null; } + if (storeConfigs != null) { + // get the list of configs. + String configList = properties.getProperty(ProjectProperties.PROPERTY_CONFIGS); + + // this is a comma separated list + String[] configs = configList.split(","); //$NON-NLS-1$ + + // read the value of each config and store it in a map + HashMap<String, String> configMap = new HashMap<String, String>(); + + for (String config : configs) { + String configValue = properties.getProperty(config); + if (configValue != null) { + configMap.put(config, configValue); + } + } + + if (configMap.size() > 0) { + storeConfigs.mProjectConfigMap.put(project, configMap); + } + } + return properties.getProperty(ProjectProperties.PROPERTY_TARGET); } + + /** + * Returns the hash string uniquely identifying the target of a project. + * <p/> + * This methods reads the content of the <code>default.properties</code> file present in + * the root folder of the project. + * <p/>The string is equivalent to the return of {@link IAndroidTarget#hashString()}. + * @param project The project for which to return the target hash string. + * @return the hash string or null if the project does not have a target set. + */ + public static String getProjectTargetHashString(IProject project) { + return loadProjectProperties(project, null /*storeConfigs*/); + } /** - * Sets a target hash string in a project's persistent preferences/property storage. + * Sets a target hash string in given project's <code>default.properties</code> file. * @param project The project in which to save the hash string. * @param targetHashString The target hash string to save. This must be the result from * {@link IAndroidTarget#hashString()}. @@ -249,12 +297,82 @@ public class Sdk { * Return the {@link AndroidTargetData} for a given {@link IAndroidTarget}. */ public AndroidTargetData getTargetData(IAndroidTarget target) { - synchronized (mTargetMap) { - return mTargetMap.get(target); + synchronized (mTargetDataMap) { + return mTargetDataMap.get(target); } } /** + * Returns the configuration map for a given project. + * <p/>The Map key are name to be used in the apk filename, while the values are comma separated + * config values. The config value can be passed directly to aapt through the -c option. + */ + public Map<String, String> getProjectConfigs(IProject project) { + return mProjectConfigMap.get(project); + } + + public void setProjectConfigs(IProject project, Map<String, String> configMap) + throws CoreException { + // first set the new map + mProjectConfigMap.put(project, configMap); + + // Now we write this in default.properties. + // Because we don't want to erase other properties from default.properties, we first load + // them + ProjectProperties properties = ProjectProperties.load(project.getLocation().toOSString(), + PropertyType.DEFAULT); + if (properties == null) { + // doesn't exist yet? we create it. + properties = ProjectProperties.create(project.getLocation().toOSString(), + PropertyType.DEFAULT); + } + + // load the current configs, in order to remove the value properties for each of them + // in case a config was removed. + + // get the list of configs. + String configList = properties.getProperty(ProjectProperties.PROPERTY_CONFIGS); + + // this is a comma separated list + String[] configs = configList.split(","); //$NON-NLS-1$ + + boolean hasRemovedConfig = false; + + for (String config : configs) { + if (configMap.containsKey(config) == false) { + hasRemovedConfig = true; + properties.removeProperty(config); + } + } + + // now add the properties. + Set<Entry<String, String>> entrySet = configMap.entrySet(); + StringBuilder sb = new StringBuilder(); + for (Entry<String, String> entry : entrySet) { + if (sb.length() > 0) { + sb.append(","); + } + sb.append(entry.getKey()); + properties.setProperty(entry.getKey(), entry.getValue()); + } + properties.setProperty(ProjectProperties.PROPERTY_CONFIGS, sb.toString()); + + // and rewrite the file. + try { + properties.save(); + } catch (IOException e) { + AdtPlugin.log(e, "Failed to save default.properties for project '%s'", + project.getName()); + } + + // we're done, force a rebuild. If there was removed config, we clean instead of build + // (to remove the obsolete ap_ and apk file from removed configs). + project.build(hasRemovedConfig ? + IncrementalProjectBuilder.CLEAN_BUILD : IncrementalProjectBuilder.FULL_BUILD, + null); + } + + /** * Returns the {@link AvdManager}. If the AvdManager failed to parse the AVD folder, this could * be <code>null</code>. */ @@ -266,14 +384,25 @@ public class Sdk { mManager = manager; mAvdManager = avdManager; + // listen to projects closing + ResourceMonitor monitor = ResourceMonitor.getMonitor(); + monitor.addProjectListener(this); + // pre-compute some paths mDocBaseUrl = getDocumentationBaseUrl(mManager.getLocation() + SdkConstants.OS_SDK_DOCS_FOLDER); } + + /** + * Cleans and unloads the SDK. + */ + private void dispose() { + ResourceMonitor.getMonitor().removeProjectListener(this); + } void setTargetData(IAndroidTarget target, AndroidTargetData data) { - synchronized (mTargetMap) { - mTargetMap.put(target, data); + synchronized (mTargetDataMap) { + mTargetDataMap.put(target, data); } } @@ -314,6 +443,24 @@ public class Sdk { return null; } + public void projectClosed(IProject project) { + mProjectTargetMap.remove(project); + mProjectConfigMap.remove(project); + } + + public void projectDeleted(IProject project) { + projectClosed(project); + } + + public void projectOpened(IProject project) { + // ignore this. The project will be added to the map the first time the target needs + // to be resolved. + } + + public void projectOpenedWithWorkspace(IProject project) { + // ignore this. The project will be added to the map the first time the target needs + // to be resolved. + } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java index 65817c3..b1c57a6 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java @@ -19,6 +19,7 @@ package com.android.ide.eclipse.common; import com.android.sdklib.SdkConstants; import java.io.File; +import java.util.regex.Pattern; /** * Constant definition class.<br> @@ -49,17 +50,6 @@ public class AndroidConstants { /** Nature of android projects */ public final static String NATURE = "com.android.ide.eclipse.adt.AndroidNature"; //$NON-NLS-1$ - public final static int PLATFORM_UNKNOWN = 0; - public final static int PLATFORM_LINUX = 1; - public final static int PLATFORM_WINDOWS = 2; - public final static int PLATFORM_DARWIN = 3; - - /** - * Returns current platform, one of {@link #PLATFORM_WINDOWS}, {@link #PLATFORM_DARWIN}, - * {@link #PLATFORM_LINUX} or {@link #PLATFORM_UNKNOWN}. - */ - public final static int CURRENT_PLATFORM = currentPlatform(); - /** Separator for workspace path, i.e. "/". */ public final static String WS_SEP = "/"; //$NON-NLS-1$ /** Separator character for workspace path, i.e. '/'. */ @@ -99,8 +89,6 @@ public class AndroidConstants { public static final String FN_ANDROID_MANIFEST = "AndroidManifest.xml"; //$NON-NLS-1$ public static final String FN_PROJECT_AIDL = "project.aidl"; //$NON-NLS-1$ - /** dex.jar file */ - public static final String FN_DX_JAR = "dx.jar"; //$NON-NLS-1$ /** Name of the android sources directory */ public static final String FD_ANDROID_SOURCES = "sources"; //$NON-NLS-1$ @@ -114,20 +102,21 @@ public class AndroidConstants { public final static String FN_CLASSES_DEX = "classes.dex"; //$NON-NLS-1$ /** Temporary packaged resources file name, i.e. "resources.ap_" */ public final static String FN_RESOURCES_AP_ = "resources.ap_"; //$NON-NLS-1$ + /** Temporary packaged resources file name for a specific set of configuration */ + public final static String FN_RESOURCES_S_AP_ = "resources-%s.ap_"; //$NON-NLS-1$ + public final static Pattern PATTERN_RESOURCES_S_AP_ = + Pattern.compile("resources-.*\\.ap_", Pattern.CASE_INSENSITIVE); - public final static String FN_ADB = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ? + public final static String FN_ADB = + (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_WINDOWS) ? "adb.exe" : "adb"; //$NON-NLS-1$ //$NON-NLS-2$ - public final static String FN_AAPT = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ? - "aapt.exe" : "aapt"; //$NON-NLS-1$ //$NON-NLS-2$ - - public final static String FN_AIDL = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ? - "aidl.exe" : "aidl"; //$NON-NLS-1$ //$NON-NLS-2$ - - public final static String FN_EMULATOR = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ? + public final static String FN_EMULATOR = + (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_WINDOWS) ? "emulator.exe" : "emulator"; //$NON-NLS-1$ //$NON-NLS-2$ - public final static String FN_TRACEVIEW = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ? + public final static String FN_TRACEVIEW = + (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_WINDOWS) ? "traceview.exe" : "traceview"; //$NON-NLS-1$ //$NON-NLS-2$ /** Absolute path of the workspace root, i.e. "/" */ @@ -147,11 +136,6 @@ public class AndroidConstants { * FIXME: remove once the NPW is fixed. */ public final static String OS_SDK_SAMPLES_FOLDER = SdkConstants.FD_SAMPLES + File.separator; - /** Path of the dx.jar file relative to the sdk folder. */ - public final static String OS_SDK_LIBS_DX_JAR = - SdkConstants.OS_SDK_TOOLS_LIB_FOLDER + FN_DX_JAR; - - /** Regexp for single dot */ public final static String RE_DOT = "\\."; //$NON-NLS-1$ /** Regexp for java extension, i.e. "\.java$" */ public final static String RE_JAVA_EXT = "\\.java$"; //$NON-NLS-1$ @@ -229,22 +213,4 @@ public class AndroidConstants { /** The base URL where to find the Android class & manifest documentation */ public static final String CODESITE_BASE_URL = "http://code.google.com/android"; //$NON-NLS-1$ - /** - * Returns current platform - * - * @return one of {@link #PLATFORM_WINDOWS}, {@link #PLATFORM_DARWIN}, - * {@link #PLATFORM_LINUX} or {@link #PLATFORM_UNKNOWN}. - */ - private static int currentPlatform() { - String os = System.getProperty("os.name"); //$NON-NLS-1$ - if (os.startsWith("Mac OS")) { //$NON-NLS-1$ - return PLATFORM_DARWIN; - } else if (os.startsWith("Windows")) { //$NON-NLS-1$ - return PLATFORM_WINDOWS; - } else if (os.startsWith("Linux")) { //$NON-NLS-1$ - return PLATFORM_LINUX; - } - - return PLATFORM_UNKNOWN; - } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/IconFactory.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/IconFactory.java index e3de3af..2c24772 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/IconFactory.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/IconFactory.java @@ -18,7 +18,7 @@ package com.android.ide.eclipse.editors; import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.common.AndroidConstants; +import com.android.sdklib.SdkConstants; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.swt.SWT; @@ -231,7 +231,7 @@ public class IconFactory { // Text measurement varies so slightly depending on the platform int ofx = 0; int ofy = 0; - if (AndroidConstants.CURRENT_PLATFORM == AndroidConstants.PLATFORM_WINDOWS) { + if (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_WINDOWS) { ofx = +1; ofy = -1; } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/explorer/ResourceExplorerView.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/explorer/ResourceExplorerView.java index 845db32..d1d8891 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/explorer/ResourceExplorerView.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/explorer/ResourceExplorerView.java @@ -160,6 +160,13 @@ public class ResourceExplorerView extends ViewPart implements ISelectionListener // set up the resource manager to send us resource change notification AdtPlugin.getDefault().getResourceMonitor().addResourceEventListener(this); } + + @Override + public void dispose() { + AdtPlugin.getDefault().getResourceMonitor().removeResourceEventListener(this); + + super.dispose(); + } @Override public void setFocus() { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ResourceMonitor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ResourceMonitor.java index dc0f505..59a72fb 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ResourceMonitor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ResourceMonitor.java @@ -189,20 +189,15 @@ public class ResourceMonitor implements IResourceChangeListener { // the project is opening or closing. IProject project = (IProject)r; - // the OPEN flag represent a toggle in the open/close state of the - // project, but this is sent before the project actually toggles - // its state. - // This means that if the project is closing, isOpen() will return true. - boolean isClosing = project.isOpen(); - if (isClosing) { + if (project.isOpen()) { // notify the listeners. for (IProjectListener pl : mProjectListeners) { - pl.projectClosed(project); + pl.projectOpened(project); } } else { // notify the listeners. for (IProjectListener pl : mProjectListeners) { - pl.projectOpened(project); + pl.projectClosed(project); } } } @@ -287,6 +282,20 @@ public class ResourceMonitor implements IResourceChangeListener { } /** + * Removes an existing folder listener. + * @param listener the listener to remove. + */ + public synchronized void removeFolderListener(IFolderListener listener) { + for (int i = 0 ; i < mFolderListeners.size() ; i++) { + FolderListenerBundle bundle = mFolderListeners.get(i); + if (bundle.listener == listener) { + mFolderListeners.remove(i); + return; + } + } + } + + /** * Adds a project listener. * @param listener The listener to receive the events. */ @@ -305,11 +314,31 @@ public class ResourceMonitor implements IResourceChangeListener { } } + /** + * Removes an existing project listener. + * @param listener the listener to remove. + */ + public synchronized void removeProjectListener(IProjectListener listener) { + mProjectListeners.remove(listener); + } + + /** + * Adds a resource event listener. + * @param listener The listener to receive the events. + */ public synchronized void addResourceEventListener(IResourceEventListener listener) { mEventListeners.add(listener); } /** + * Removes an existing Resource Event listener. + * @param listener the listener to remove. + */ + public synchronized void removeResourceEventListener(IResourceEventListener listener) { + mEventListeners.remove(listener); + } + + /** * Processes the workspace resource change events. */ public void resourceChanged(IResourceChangeEvent event) { diff --git a/eclipse/scripts/collect_sources_for_sdk.sh b/eclipse/scripts/collect_sources_for_sdk.sh new file mode 100644 index 0000000..4637595 --- /dev/null +++ b/eclipse/scripts/collect_sources_for_sdk.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +function usage() { + cat <<EOF + Description: + This script collects all framework Java sources from the current android + source code and places them in a source folder suitable for the eclipse ADT + plugin. + + Usage: + $0 [-n] <android-git-repo root> <sdk/platforms/xyz/sources> + + The source and destination directories must already exist. + Use -n for a dry-run. + +EOF +} + +DRY="" +if [ "-n" == "$1" ]; then + DRY="echo" + shift +fi + +SRC="$1" +DST="$2" + +if [ -z "$SRC" ] || [ -z "$DST" ] || [ ! -d "$SRC" ] || [ ! -d "$DST" ]; then + usage + exit 1 +fi + +function process() { + echo "Examine" $1 +} + +N=0 +E=0 +for i in `find -L "${SRC}/frameworks" -name "*.java"`; do + if [ -f "$i" ]; then + # look for ^package (android.view.blah);$ + PACKAGE=`sed -n '/^package [^ ;]\+; */{s/[^ ]* *\([^ ;]*\).*/\1/p;q}' "$i"` + if [ -n "$PACKAGE" ]; then + PACKAGE=${PACKAGE//./\/} # e.g. android.view => android/view + JAVA=`basename "$i"` # e.g. View.java + [ -z $DRY ] && [ ! -d "$DST/$PACKAGE" ] && mkdir -p -v "$DST/$PACKAGE" + $DRY cp -v "$i" "$DST/$PACKAGE/$JAVA" + N=$((N+1)) + else + echo "Warning: $i does not have a Java package." + E=$((E+1)) + fi + fi +done + +echo "$N java files copied" +[ $E -gt 0 ] && echo "$E warnings" + diff --git a/scripts/android_rules.xml b/scripts/android_rules.xml index 8e4f7a2..4698671 100644 --- a/scripts/android_rules.xml +++ b/scripts/android_rules.xml @@ -1,6 +1,18 @@ <?xml version="1.0" encoding="UTF-8"?> <project name="android_rules" default="debug"> + <!-- + This rules file is meant to be imported by the custom Ant task: + com.android.ant.AndroidInitTask + + The following properties are put in place by the importing task: + android-jar, android-aidl, aapt, aidl, and dx + + Additionnaly, the task sets up the following classpath reference: + android.target.classpath + This is used by the compiler task as the boot classpath. + --> + <property name="android-tools" value="${sdk-location}/tools" /> <!-- Input directories --> @@ -55,13 +67,10 @@ <condition property="exe" value="exe" else=""><os family="windows"/></condition> <condition property="bat" value="bat" else=""><os family="windows"/></condition> - <property name="aapt" value="${android-tools}/aapt${exe}"/> - <property name="aidl" value="${android-tools}/aidl${exe}"/> <property name="adb" value="${android-tools}/adb${exe}"/> - <property name="dx" value="${android-tools}/dx${bat}"/> <property name="apk-builder" value="${android-tools}/apkbuilder${bat}"/> - <!-- rules --> + <!-- rules --> <!-- Create the output directories if they don't exist yet. --> <target name="dirs"> diff --git a/sdkmanager/app/src/com/android/sdkmanager/Main.java b/sdkmanager/app/src/com/android/sdkmanager/Main.java index 3370e76..1a15fce 100644 --- a/sdkmanager/app/src/com/android/sdkmanager/Main.java +++ b/sdkmanager/app/src/com/android/sdkmanager/Main.java @@ -356,6 +356,7 @@ class Main { */ private void displaySkinList(IAndroidTarget target, String message) { String[] skins = target.getSkins(); + String defaultSkin = target.getDefaultSkin(); mSdkLog.printf(message); if (skins != null) { boolean first = true; @@ -366,6 +367,10 @@ class Main { first = false; } mSdkLog.printf(skin); + + if (skin.equals(defaultSkin)) { + mSdkLog.printf(" (default)"); + } } mSdkLog.printf("\n"); } else { @@ -385,7 +390,7 @@ class Main { int index = 1; for (AvdInfo info : avdManager.getAvds()) { mSdkLog.printf("[%d] %s\n", index, info.getName()); - mSdkLog.printf(" Path: %s\n", info.getPath()); + mSdkLog.printf(" Path: %s\n", info.getPath()); // get the target of the AVD IAndroidTarget target = info.getTarget(); @@ -395,9 +400,23 @@ class Main { } else { mSdkLog.printf(" Target: %s (%s)\n", target.getName(), target .getVendor()); - mSdkLog.printf(" Based on Android %s (API level %d)\n", target + mSdkLog.printf(" Based on Android %s (API level %d)\n", target .getApiVersionName(), target.getApiVersionNumber()); } + + // display some extra values. + Map<String, String> properties = info.getProperties(); + String skin = properties.get(AvdManager.AVD_INI_SKIN_NAME); + if (skin != null) { + mSdkLog.printf(" Skin: %s\n", skin); + } + String sdcard = properties.get(AvdManager.AVD_INI_SDCARD_SIZE); + if (sdcard == null) { + sdcard = properties.get(AvdManager.AVD_INI_SDCARD_PATH); + } + if (sdcard != null) { + mSdkLog.printf(" Sdcard: %s\n", sdcard); + } index++; } @@ -489,7 +508,7 @@ class Main { return; } } - + AvdInfo newAvdInfo = avdManager.createAvd(avdFolder, avdName, target, diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java index ada61f7..0a59107 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java @@ -69,6 +69,7 @@ final class AddOnTarget implements IAndroidTarget { private final String mVendor; private final String mDescription; private String[] mSkins; + private String mDefaultSkin; private IOptionalLibrary[] mLibraries; /** @@ -141,6 +142,10 @@ final class AddOnTarget implements IAndroidTarget { return false; } + public IAndroidTarget getParent() { + return mBasePlatform; + } + public String getPath(int pathId) { switch (pathId) { case IMAGES: @@ -157,6 +162,10 @@ final class AddOnTarget implements IAndroidTarget { public String[] getSkins() { return mSkins; } + + public String getDefaultSkin() { + return mDefaultSkin; + } public IOptionalLibrary[] getOptionalLibraries() { return mLibraries; @@ -236,7 +245,9 @@ final class AddOnTarget implements IAndroidTarget { // ---- local methods. - public void setSkins(String[] skins) { + public void setSkins(String[] skins, String defaultSkin) { + mDefaultSkin = defaultSkin; + // we mix the add-on and base platform skins HashSet<String> skinSet = new HashSet<String>(); skinSet.addAll(Arrays.asList(skins)); diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java index acf1187..fa462bd 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java @@ -60,6 +60,14 @@ public interface IAndroidTarget extends Comparable<IAndroidTarget> { public static int SOURCES = 18; /** OS Path to the target specific docs */ public static int DOCS = 19; + /** OS Path to the target's version of the aapt tool. */ + public static int AAPT = 20; + /** OS Path to the target's version of the aidl tool. */ + public static int AIDL = 21; + /** OS Path to the target's version of the dx too. */ + public static int DX = 22; + /** OS Path to the target's version of the dx.jar file. */ + public static int DX_JAR = 23; public interface IOptionalLibrary { String getName(); @@ -109,6 +117,12 @@ public interface IAndroidTarget extends Comparable<IAndroidTarget> { boolean isPlatform(); /** + * Returns the parent target. This is likely to only be non <code>null</code> if + * {@link #isPlatform()} returns <code>false</code> + */ + IAndroidTarget getParent(); + + /** * Returns the path of a platform component. * @param pathId the id representing the path to return. Any of the constants defined in the * {@link IAndroidTarget} interface can be used. @@ -121,6 +135,11 @@ public interface IAndroidTarget extends Comparable<IAndroidTarget> { String[] getSkins(); /** + * Returns the default skin for this target. + */ + String getDefaultSkin(); + + /** * Returns the available optional libraries for this target. * @return an array of optional libraries or <code>null</code> if there is none. */ diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java index 59fa81c..a3da70e 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java @@ -75,8 +75,13 @@ final class PlatformTarget implements IAndroidTarget { SdkConstants.FN_INTENT_ACTIONS_SERVICE); mPaths.put(CATEGORIES, mLocation + SdkConstants.OS_PLATFORM_DATA_FOLDER + SdkConstants.FN_INTENT_CATEGORIES); + mPaths.put(AAPT, mLocation + SdkConstants.OS_SDK_TOOLS_FOLDER + SdkConstants.FN_AAPT); + mPaths.put(AIDL, mLocation + SdkConstants.OS_SDK_TOOLS_FOLDER + SdkConstants.FN_AIDL); + mPaths.put(DX, mLocation + SdkConstants.OS_SDK_TOOLS_FOLDER + SdkConstants.FN_DX); + mPaths.put(DX_JAR, mLocation + SdkConstants.OS_SDK_TOOLS_LIB_FOLDER + + SdkConstants.FN_DX_JAR); } - + public String getLocation() { return mLocation; } @@ -123,6 +128,10 @@ final class PlatformTarget implements IAndroidTarget { return true; } + public IAndroidTarget getParent() { + return null; + } + public String getPath(int pathId) { return mPaths.get(pathId); } @@ -130,6 +139,11 @@ final class PlatformTarget implements IAndroidTarget { public String[] getSkins() { return mSkins; } + + public String getDefaultSkin() { + // at this time, this is the default skin for all the platform. + return "HVGA"; + } /* * Always returns null, as a standard platforms have no optional libraries. diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java index b79dedb..87f9b56 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java @@ -30,7 +30,18 @@ import java.io.File; * */ public final class SdkConstants { + public final static int PLATFORM_UNKNOWN = 0; + public final static int PLATFORM_LINUX = 1; + public final static int PLATFORM_WINDOWS = 2; + public final static int PLATFORM_DARWIN = 3; + /** + * Returns current platform, one of {@link #PLATFORM_WINDOWS}, {@link #PLATFORM_DARWIN}, + * {@link #PLATFORM_LINUX} or {@link #PLATFORM_UNKNOWN}. + */ + public final static int CURRENT_PLATFORM = currentPlatform(); + + /** An SDK Project's AndroidManifest.xml file */ public static final String FN_ANDROID_MANIFEST_XML= "AndroidManifest.xml"; /** An SDK Project's build.xml file */ @@ -69,6 +80,21 @@ public final class SdkConstants { /** Skin layout file */ public final static String FN_SKIN_LAYOUT = "layout";//$NON-NLS-1$ + /** dex.jar file */ + public static final String FN_DX_JAR = "dx.jar"; //$NON-NLS-1$ + + /** dx executable */ + public final static String FN_DX = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ? + "dx.bat" : "dx"; //$NON-NLS-1$ //$NON-NLS-2$ + + /** aapt executable */ + public final static String FN_AAPT = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ? + "aapt.exe" : "aapt"; //$NON-NLS-1$ //$NON-NLS-2$ + + /** aidl executable */ + public final static String FN_AIDL = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ? + "aidl.exe" : "aidl"; //$NON-NLS-1$ //$NON-NLS-2$ + /* Folder Names for Android Projects . */ /** Resources folder name, i.e. "res". */ @@ -142,11 +168,11 @@ public final class SdkConstants { * This is an OS path, ending with a separator. */ public final static String OS_SDK_DOCS_FOLDER = FD_DOCS + File.separator; - /** Path of the tools directory relative to the sdk folder. + /** Path of the tools directory relative to the sdk folder, or to a platform folder. * This is an OS path, ending with a separator. */ public final static String OS_SDK_TOOLS_FOLDER = FD_TOOLS + File.separator; - /** Path of the lib directory relative to the sdk folder. + /** Path of the lib directory relative to the sdk folder, or to a platform folder. * This is an OS path, ending with a separator. */ public final static String OS_SDK_TOOLS_LIB_FOLDER = OS_SDK_TOOLS_FOLDER + FD_LIB + File.separator; @@ -233,4 +259,22 @@ public final class SdkConstants { return cmd; } + /** + * Returns current platform + * + * @return one of {@link #PLATFORM_WINDOWS}, {@link #PLATFORM_DARWIN}, + * {@link #PLATFORM_LINUX} or {@link #PLATFORM_UNKNOWN}. + */ + private static int currentPlatform() { + String os = System.getProperty("os.name"); //$NON-NLS-1$ + if (os.startsWith("Mac OS")) { //$NON-NLS-1$ + return PLATFORM_DARWIN; + } else if (os.startsWith("Windows")) { //$NON-NLS-1$ + return PLATFORM_WINDOWS; + } else if (os.startsWith("Linux")) { //$NON-NLS-1$ + return PLATFORM_LINUX; + } + + return PLATFORM_UNKNOWN; + } } diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java index f6fb622..28227c6 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java @@ -44,6 +44,7 @@ public final class SdkManager { private final static String ADDON_API = "api"; private final static String ADDON_DESCRIPTION = "description"; private final static String ADDON_LIBRARIES = "libraries"; + private final static String ADDON_DEFAULT_SKIN = "skin"; private final static Pattern PATTERN_PROP = Pattern.compile( "^([a-zA-Z0-9._-]+)\\s*=\\s*(.*)\\s*$"); @@ -51,6 +52,17 @@ public final class SdkManager { private final static Pattern PATTERN_LIB_DATA = Pattern.compile( "^([a-zA-Z0-9._-]+\\.jar);(.*)$", Pattern.CASE_INSENSITIVE); + /** List of items in the platform to check when parsing it. These paths are relative to the + * platform root folder. */ + private final static String[] sPlatformContentList = new String[] { + SdkConstants.FN_FRAMEWORK_LIBRARY, + SdkConstants.FN_FRAMEWORK_AIDL, + SdkConstants.OS_SDK_TOOLS_FOLDER + SdkConstants.FN_AAPT, + SdkConstants.OS_SDK_TOOLS_FOLDER + SdkConstants.FN_AIDL, + SdkConstants.OS_SDK_TOOLS_FOLDER + SdkConstants.FN_DX, + SdkConstants.OS_SDK_TOOLS_LIB_FOLDER + SdkConstants.FN_DX_JAR, + }; + /** the location of the SDK */ private final String mSdkLocation; private IAndroidTarget[] mTargets; @@ -176,6 +188,10 @@ public final class SdkManager { String apiNumber = map.get(PROP_VERSION_SDK); String apiName = map.get(PROP_VERSION_RELEASE); if (apiNumber != null && apiName != null) { + // api number and name looks valid, perform a few more checks + if (checkPlatformContent(platform, log) == false) { + return null; + } // create the target. PlatformTarget target = new PlatformTarget( platform.getAbsolutePath(), @@ -351,7 +367,19 @@ public final class SdkManager { // need to parse the skins. String[] skins = parseSkinFolder(target.getPath(IAndroidTarget.SKINS)); - target.setSkins(skins); + + // get the default skin, or take it from the base platform if needed. + String defaultSkin = propertyMap.get(ADDON_DEFAULT_SKIN); + + if (defaultSkin == null) { + if (skins.length == 1) { + defaultSkin = skins[1]; + } else { + defaultSkin = baseTarget.getDefaultSkin(); + } + } + + target.setSkins(skins, defaultSkin); return target; } @@ -369,7 +397,28 @@ public final class SdkManager { addonName, valueName, SdkConstants.FN_MANIFEST_INI); } } + + /** + * Checks the given platform has all the required files, and returns true if they are all + * present. + * <p/>This checks the presence of the following files: android.jar, framework.aidl, aapt(.exe), + * aidl(.exe), dx(.bat), and dx.jar + */ + private boolean checkPlatformContent(File platform, ISdkLog log) { + for (String relativePath : sPlatformContentList) { + File f = new File(platform, relativePath); + if (f.exists() == false) { + log.error(null, + "Ignoring platform '%1$s': %2$s is missing.", + platform.getName(), relativePath); + return false; + } + + } + return true; + } + /** * Parses a property file and returns * @param buildProp the property file to parse diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java index d466182..b44cf01 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java @@ -44,28 +44,42 @@ import java.util.regex.Pattern; public final class AvdManager { public static final String AVD_FOLDER_EXTENSION = ".avd"; + private final static String AVD_INFO_PATH = "path"; private final static String AVD_INFO_TARGET = "target"; - - private final static String IMAGE_USERDATA = "userdata.img"; + + public final static String AVD_INI_SKIN_PATH = "skin.path"; + public final static String AVD_INI_SKIN_NAME = "skin.name"; + public final static String AVD_INI_SDCARD_PATH = "sdcard.path"; + public final static String AVD_INI_SDCARD_SIZE = "sdcard.size"; + public final static String AVD_INI_IMAGES_1 = "image.sysdir.1"; + public final static String AVD_INI_IMAGES_2 = "image.sysdir.2"; + + private final static String USERDATA_IMG = "userdata.img"; private final static String CONFIG_INI = "config.ini"; + private final static String SDCARD_IMG = "sdcard.img"; - private final static Pattern INI_NAME_PATTERN = Pattern.compile("(.+)\\.ini$", + private final static String INI_EXTENSION = ".ini"; + private final static Pattern INI_NAME_PATTERN = Pattern.compile("(.+)\\" + INI_EXTENSION + "$", Pattern.CASE_INSENSITIVE); private final static Pattern SDCARD_SIZE_PATTERN = Pattern.compile("\\d+[MK]?"); /** An immutable structure describing an Android Virtual Device. */ public static final class AvdInfo { - private String mName; - private String mPath; - private IAndroidTarget mTarget; + private final String mName; + private final String mPath; + private final IAndroidTarget mTarget; + private final Map<String, String> mProperties; - /** Creates a new AVD info. Valures are immutable. */ - public AvdInfo(String name, String path, IAndroidTarget target) { + /** Creates a new AVD info. Valures are immutable. + * @param properties */ + public AvdInfo(String name, String path, IAndroidTarget target, + Map<String, String> properties) { mName = name; mPath = path; mTarget = target; + mProperties = properties; } /** Returns the name of the AVD. */ @@ -90,7 +104,7 @@ public final class AvdManager { public static File getIniFile(String name) throws AndroidLocationException { String avdRoot; avdRoot = AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD; - return new File(avdRoot, name + ".ini"); + return new File(avdRoot, name + INI_EXTENSION); } /** @@ -100,6 +114,13 @@ public final class AvdManager { public File getIniFile() throws AndroidLocationException { return getIniFile(mName); } + + /** + * Returns a map of properties for the AVD. + */ + public Map<String, String> getProperties() { + return mProperties; + } } private final ArrayList<AvdInfo> mAvdList = new ArrayList<AvdInfo>(); @@ -149,6 +170,8 @@ public final class AvdManager { String skinName, String sdcard, Map<String,String> hardwareConfig, boolean removePrevious, ISdkLog log) { + File iniFile = null; + boolean needCleanup = false; try { if (avdFolder.exists()) { if (removePrevious) { @@ -170,14 +193,27 @@ public final class AvdManager { } // actually write the ini file - createAvdIniFile(name, avdFolder, target); + iniFile = createAvdIniFile(name, avdFolder, target); // writes the userdata.img in it. String imagePath = target.getPath(IAndroidTarget.IMAGES); - File userdataSrc = new File(imagePath, IMAGE_USERDATA); + File userdataSrc = new File(imagePath, USERDATA_IMG); + + if (userdataSrc.exists() == false && target.isPlatform() == false) { + imagePath = target.getParent().getPath(IAndroidTarget.IMAGES); + userdataSrc = new File(imagePath, USERDATA_IMG); + } + + if (userdataSrc.exists() == false) { + log.error(null, "Unable to find a '%1$s' file to copy into the AVD folder.", + USERDATA_IMG); + needCleanup = true; + return null; + } + FileInputStream fis = new FileInputStream(userdataSrc); - File userdataDest = new File(avdFolder, IMAGE_USERDATA); + File userdataDest = new File(avdFolder, USERDATA_IMG); FileOutputStream fos = new FileOutputStream(userdataDest); byte[] buffer = new byte[4096]; @@ -189,23 +225,61 @@ public final class AvdManager { fos.close(); fis.close(); - // Config file + // Config file. HashMap<String, String> values = new HashMap<String, String>(); - if (skinName != null) { - // assume skin name is valid - values.put("skin", skinName); + + // First the image folders of the target itself + imagePath = getImageRelativePath(target, log); + if (imagePath == null) { + needCleanup = true; + return null; } + + values.put(AVD_INI_IMAGES_1, imagePath); + + // If the target is an add-on we need to add the Platform image as a backup. + IAndroidTarget parent = target.getParent(); + if (parent != null) { + imagePath = getImageRelativePath(parent, log); + if (imagePath == null) { + needCleanup = true; + return null; + } + + values.put(AVD_INI_IMAGES_2, imagePath); + } + + + // Now the skin. + if (skinName == null) { + skinName = target.getDefaultSkin(); + } + + // get the path of the skin (relative to the SDK) + // assume skin name is valid + String skinPath = getSkinRelativePath(skinName, target, log); + if (skinPath == null) { + needCleanup = true; + return null; + } + + values.put(AVD_INI_SKIN_PATH, skinPath); + values.put(AVD_INI_SKIN_NAME, skinName); if (sdcard != null) { File sdcardFile = new File(sdcard); if (sdcardFile.isFile()) { - values.put("sdcard", sdcard); + // sdcard value is an external sdcard, so we put its path into the config.ini + values.put(AVD_INI_SDCARD_PATH, sdcard); } else { - // check that it matches the pattern for sdcard size + // Sdcard is possibly a size. In that case we create a file called 'sdcard.img' + // in the AVD folder, and do not put any value in config.ini. + + // First, check that it matches the pattern for sdcard size Matcher m = SDCARD_SIZE_PATTERN.matcher(sdcard); if (m.matches()) { // create the sdcard. - sdcardFile = new File(avdFolder, "sdcard.img"); + sdcardFile = new File(avdFolder, SDCARD_IMG); String path = sdcardFile.getAbsolutePath(); // execute mksdcard with the proper parameters. @@ -215,18 +289,27 @@ public final class AvdManager { if (mkSdCard.isFile() == false) { log.error(null, "'%1$s' is missing from the SDK tools folder.", mkSdCard.getName()); + needCleanup = true; return null; } if (createSdCard(mkSdCard.getAbsolutePath(), sdcard, path, log) == false) { + needCleanup = true; return null; // mksdcard output has already been displayed, no need to // output anything else. } - // add its path to the values. - values.put("sdcard", path); + // add a property containing the size of the sdcard for display purpose + // only when the dev does 'android list avd' + values.put(AVD_INI_SDCARD_SIZE, sdcard); } else { - log.error(null, "'%1$s' is not recognized as a valid sdcard value", sdcard); + log.error(null, + "'%1$s' is not recognized as a valid sdcard value.\n" + + "Value should be:\n" + + "1. path to an sdcard.\n" + + "2. size of the sdcard to create: <size>[K|M]", + sdcard); + needCleanup = true; return null; } } @@ -249,7 +332,7 @@ public final class AvdManager { } // create the AvdInfo object, and add it to the list - AvdInfo avdInfo = new AvdInfo(name, avdFolder.getAbsolutePath(), target); + AvdInfo avdInfo = new AvdInfo(name, avdFolder.getAbsolutePath(), target, values); mAvdList.add(avdInfo); @@ -262,12 +345,84 @@ public final class AvdManager { if (log != null) { log.error(e, null); } + } finally { + if (needCleanup) { + if (iniFile != null && iniFile.exists()) { + iniFile.delete(); + } + + recursiveDelete(avdFolder); + avdFolder.delete(); + } } return null; } /** + * Returns the path to the target images folder as a relative path to the SDK. + */ + private String getImageRelativePath(IAndroidTarget target, ISdkLog log) { + String imageFullPath = target.getPath(IAndroidTarget.IMAGES); + + // make this path relative to the SDK location + String sdkLocation = mSdk.getLocation(); + if (imageFullPath.startsWith(sdkLocation) == false) { + // this really really should not happen. + log.error(null, "Target location is not inside the SDK."); + assert false; + return null; + } + + imageFullPath = imageFullPath.substring(sdkLocation.length()); + if (imageFullPath.charAt(0) == File.separatorChar) { + imageFullPath = imageFullPath.substring(1); + } + return imageFullPath; + } + + /** + * Returns the path to the skin, as a relative path to the SDK. + */ + private String getSkinRelativePath(String skinName, IAndroidTarget target, ISdkLog log) { + // first look to see if the skin is in the target + + String path = target.getPath(IAndroidTarget.SKINS); + File skin = new File(path, skinName); + + if (skin.exists() == false && target.isPlatform() == false) { + target = target.getParent(); + + path = target.getPath(IAndroidTarget.SKINS); + skin = new File(path, skinName); + } + + // skin really does not exist! + if (skin.exists() == false) { + log.error(null, "Skin '%1$s' does not exist.", skinName); + return null; + } + + // get the skin path + path = skin.getAbsolutePath(); + + // make this path relative to the SDK location + String sdkLocation = mSdk.getLocation(); + if (path.startsWith(sdkLocation) == false) { + // this really really should not happen. + log.error(null, "Target location is not inside the SDK."); + assert false; + return null; + } + + path = path.substring(sdkLocation.length()); + if (path.charAt(0) == File.separatorChar) { + path = path.substring(1); + } + return path; + } + + /** * Creates the ini file for an AVD. * * @param name of the AVD. @@ -276,13 +431,15 @@ public final class AvdManager { * @throws AndroidLocationException if there's a problem getting android root directory. * @throws IOException if {@link File#getAbsolutePath()} fails. */ - private void createAvdIniFile(String name, File avdFolder, IAndroidTarget target) + private File createAvdIniFile(String name, File avdFolder, IAndroidTarget target) throws AndroidLocationException, IOException { HashMap<String, String> values = new HashMap<String, String>(); File iniFile = AvdInfo.getIniFile(name); values.put(AVD_INFO_PATH, avdFolder.getAbsolutePath()); values.put(AVD_INFO_TARGET, target.hashString()); createConfigIni(iniFile, values); + + return iniFile; } /** @@ -292,8 +449,8 @@ public final class AvdManager { * @throws AndroidLocationException if there's a problem getting android root directory. * @throws IOException if {@link File#getAbsolutePath()} fails. */ - private void createAvdIniFile(AvdInfo info) throws AndroidLocationException, IOException { - createAvdIniFile(info.getName(), new File(info.getPath()), info.getTarget()); + private File createAvdIniFile(AvdInfo info) throws AndroidLocationException, IOException { + return createAvdIniFile(info.getName(), new File(info.getPath()), info.getTarget()); } /** @@ -306,8 +463,6 @@ public final class AvdManager { */ public void deleteAvd(AvdInfo avdInfo, ISdkLog log) { try { - String avdRoot = AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD; - File f = avdInfo.getIniFile(); if (f.exists()) { log.warning("Deleting file %s", f.getCanonicalPath()); @@ -359,7 +514,8 @@ public final class AvdManager { } // update avd info - AvdInfo info = new AvdInfo(avdInfo.getName(), paramFolderPath, avdInfo.getTarget()); + AvdInfo info = new AvdInfo(avdInfo.getName(), paramFolderPath, avdInfo.getTarget(), + avdInfo.getProperties()); mAvdList.remove(avdInfo); mAvdList.add(info); avdInfo = info; @@ -380,7 +536,8 @@ public final class AvdManager { } // update avd info - AvdInfo info = new AvdInfo(newName, avdInfo.getPath(), avdInfo.getTarget()); + AvdInfo info = new AvdInfo(newName, avdInfo.getPath(), avdInfo.getTarget(), + avdInfo.getProperties()); mAvdList.remove(avdInfo); mAvdList.add(info); } @@ -458,18 +615,22 @@ public final class AvdManager { if (target == null) { return null; } + + // load the avd properties. + File configIniFile = new File(avdPath, CONFIG_INI); + Map<String, String> properties = SdkManager.parsePropertyFile(configIniFile, mSdkLog); Matcher matcher = INI_NAME_PATTERN.matcher(path.getName()); AvdInfo info = new AvdInfo( matcher.matches() ? matcher.group(1) : path.getName(), // should not happen avdPath, - target - ); + target, + properties); return info; } - + private static void createConfigIni(File iniFile, Map<String, String> values) throws IOException { FileWriter writer = new FileWriter(iniFile); diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectProperties.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectProperties.java index 4e1c27f..1f6a047 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectProperties.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectProperties.java @@ -33,6 +33,7 @@ import java.util.Map.Entry; public final class ProjectProperties { /** The property name for the project target */ public final static String PROPERTY_TARGET = "target"; + public final static String PROPERTY_CONFIGS = "configs"; public final static String PROPERTY_SDK = "sdk-location"; public static enum PropertyType { @@ -201,6 +202,14 @@ public final class ProjectProperties { public String getProperty(String name) { return mProperties.get(name); } + + /** + * Removes a property and returns its previous value (or null if the property did not exist). + * @param name the name of the property to remove. + */ + public String removeProperty(String name) { + return mProperties.remove(name); + } /** * Saves the property file. |