diff options
28 files changed, 827 insertions, 531 deletions
diff --git a/anttasks/src/com/android/ant/SetupTask.java b/anttasks/src/com/android/ant/SetupTask.java index 60f8c7e..7af9611 100644 --- a/anttasks/src/com/android/ant/SetupTask.java +++ b/anttasks/src/com/android/ant/SetupTask.java @@ -44,13 +44,13 @@ import java.util.HashSet; * <li>Imports the build rules located in the resolved target so that the build actually does * something. This can be disabled with the attribute <var>import</var> set to <code>false</code> * </li></ul> - * + * * This is used in build.xml/template. * */ public final class SetupTask extends ImportTask { private final static String ANDROID_RULES = "android_rules.xml"; - + // ant property with the path to the android.jar private final static String PROPERTY_ANDROID_JAR = "android-jar"; // ant property with the path to the framework.jar @@ -63,21 +63,21 @@ public final class SetupTask extends ImportTask { 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"; - + private boolean mDoImport = true; @Override public void execute() throws BuildException { Project antProject = getProject(); - + // get the SDK location String sdkLocation = antProject.getProperty(ProjectProperties.PROPERTY_SDK); - + // check if it's valid and exists if (sdkLocation == null || sdkLocation.length() == 0) { throw new BuildException("SDK Location is not set."); } - + File sdk = new File(sdkLocation); if (sdk.isDirectory() == false) { throw new BuildException(String.format("SDK Location '%s' is not valid.", sdkLocation)); @@ -120,20 +120,20 @@ public final class SetupTask extends ImportTask { // resolve it IAndroidTarget androidTarget = manager.getTargetFromHashString(targetHashString); - + if (androidTarget == null) { throw new BuildException(String.format( "Unable to resolve target '%s'", targetHashString)); } - + // display it 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.getVersionName()); } - System.out.println("API level: " + androidTarget.getApiVersionNumber()); - + System.out.println("API level: " + androidTarget.getVersion().getApiString()); + // sets up the properties to find android.jar/framework.aidl/target tools String androidJar = androidTarget.getPath(IAndroidTarget.ANDROID_JAR); antProject.setProperty(PROPERTY_ANDROID_JAR, androidJar); @@ -152,7 +152,7 @@ public final class SetupTask extends ImportTask { // create a PathElement for the framework jar PathElement element = bootclasspath.createPathElement(); element.setPath(androidJar); - + // create PathElement for each optional library. IOptionalLibrary[] libraries = androidTarget.getOptionalLibraries(); if (libraries != null) { @@ -167,13 +167,13 @@ public final class SetupTask extends ImportTask { } } } - + // finally sets the path in the project with a reference antProject.addReference(REF_CLASSPATH, bootclasspath); // find the file to import, and import it. String templateFolder = androidTarget.getPath(IAndroidTarget.TEMPLATES); - + // Now the import section. This is only executed if the task actually has to import a file. if (mDoImport) { // make sure the file exists. @@ -182,17 +182,17 @@ public final class SetupTask extends ImportTask { throw new BuildException(String.format("Template directory '%s' is missing.", templateFolder)); } - + // now check the rules file exists. File rules = new File(templateFolder, ANDROID_RULES); if (rules.isFile() == false) { throw new BuildException(String.format("Build rules file '%s' is missing.", templateFolder)); } - + // set the file location to import setFile(rules.getAbsolutePath()); - + // and import super.execute(); } diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/IDevice.java b/ddms/libs/ddmlib/src/com/android/ddmlib/IDevice.java index 7e90878..8096abd 100755 --- a/ddms/libs/ddmlib/src/com/android/ddmlib/IDevice.java +++ b/ddms/libs/ddmlib/src/com/android/ddmlib/IDevice.java @@ -28,8 +28,11 @@ import java.util.Map; public interface IDevice { public final static String PROP_BUILD_VERSION = "ro.build.version.release"; - public final static String PROP_BUILD_VERSION_NUMBER = "ro.build.version.sdk"; + public final static String PROP_BUILD_API_LEVEL = "ro.build.version.sdk"; + public final static String PROP_BUILD_CODENAME = "ro.build.version.codename"; + public final static String PROP_DEBUGGABLE = "ro.debuggable"; + /** Serial number of the first connected emulator. */ public final static String FIRST_EMULATOR_SN = "emulator-5554"; //$NON-NLS-1$ /** Device change bit mask: {@link DeviceState} change. */ @@ -39,6 +42,9 @@ public interface IDevice { /** Device change bit mask: build info change. */ public static final int CHANGE_BUILD_INFO = 0x0004; + /** @deprecated Use {@link #PROP_BUILD_API_LEVEL}. */ + public final static String PROP_BUILD_VERSION_NUMBER = PROP_BUILD_API_LEVEL; + /** * The state of a device. */ diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/PreCompilerBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/PreCompilerBuilder.java index 711708f..c7fd289 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/PreCompilerBuilder.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/PreCompilerBuilder.java @@ -24,6 +24,7 @@ import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; import com.android.ide.eclipse.adt.internal.project.FixLaunchConfig; import com.android.ide.eclipse.adt.internal.project.XmlErrorHandler.BasicXmlErrorListener; import com.android.ide.eclipse.adt.internal.sdk.Sdk; +import com.android.sdklib.AndroidVersion; import com.android.sdklib.IAndroidTarget; import com.android.sdklib.SdkConstants; @@ -89,22 +90,22 @@ public class PreCompilerBuilder extends BaseBuilder { this.sourceFolder = sourceFolder; this.aidlFile = aidlFile; } - + @Override public boolean equals(Object obj) { if (this == obj) { return true; } - + if (obj instanceof AidlData) { AidlData file = (AidlData)obj; return aidlFile.equals(file.aidlFile) && sourceFolder.equals(file.sourceFolder); } - + return false; } } - + /** * Resource Compile flag. This flag is reset to false after each successful compilation, and * stored in the project persistent properties. This allows the builder to remember its state @@ -120,7 +121,7 @@ public class PreCompilerBuilder extends BaseBuilder { /** cache of the java package defined in the manifest */ private String mManifestPackage; - + /** Output folder for generated Java File. Created on the Builder init * @see #startupOnInitialize() */ @@ -145,11 +146,11 @@ public class PreCompilerBuilder extends BaseBuilder { private boolean mDone = false; public DerivedProgressMonitor() { } - + void addFile(IFile file) { mFileList.add(file); } - + void reset() { mFileList.clear(); mDone = false; @@ -198,7 +199,7 @@ public class PreCompilerBuilder extends BaseBuilder { public PreCompilerBuilder() { super(); } - + // build() returns a list of project from which this project depends for future compilation. @SuppressWarnings("unchecked") @Override @@ -209,24 +210,24 @@ public class PreCompilerBuilder extends BaseBuilder { // First thing we do is go through the resource delta to not // lose it if we have to abort the build for any reason. - + // get the project objects IProject project = getProject(); - + // Top level check to make sure the build can move forward. abortOnBadSetup(project); - + IJavaProject javaProject = JavaCore.create(project); IAndroidTarget projectTarget = Sdk.getCurrent().getTarget(project); - + // now we need to get the classpath list ArrayList<IPath> sourceFolderPathList = BaseProjectHelper.getSourceClasspaths( javaProject); - + PreCompilerDeltaVisitor dv = null; String javaPackage = null; - int minSdkVersion = AndroidManifestParser.INVALID_MIN_SDK; - + String minSdkVersion = null; + if (kind == FULL_BUILD) { AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, Messages.Start_Full_Pre_Compiler); @@ -235,7 +236,7 @@ public class PreCompilerBuilder extends BaseBuilder { } else { AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, Messages.Start_Inc_Pre_Compiler); - + // Go through the resources and see if something changed. // Even if the mCompileResources flag is true from a previously aborted // build, we need to go through the Resource delta to get a possible @@ -247,10 +248,10 @@ public class PreCompilerBuilder extends BaseBuilder { } else { dv = new PreCompilerDeltaVisitor(this, sourceFolderPathList); delta.accept(dv); - + // record the state mMustCompileResources |= dv.getCompileResources(); - + if (dv.getForceAidlCompile()) { buildAidlCompilationList(project, sourceFolderPathList); } else { @@ -258,46 +259,46 @@ public class PreCompilerBuilder extends BaseBuilder { mergeAidlFileModifications(dv.getAidlToCompile(), dv.getAidlToRemove()); } - + // get the java package from the visitor javaPackage = dv.getManifestPackage(); minSdkVersion = dv.getMinSdkVersion(); } } - + // store the build status in the persistent storage saveProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES , mMustCompileResources); - + // if there was some XML errors, we just return w/o doing // anything since we've put some markers in the files anyway. if (dv != null && dv.mXmlError) { AdtPlugin.printErrorToConsole(project, Messages.Xml_Error); - + // This interrupts the build. The next builders will not run. stopBuild(Messages.Xml_Error); } - - + + // get the manifest file IFile manifest = AndroidManifestParser.getManifest(project); - + if (manifest == null) { String msg = String.format(Messages.s_File_Missing, AndroidConstants.FN_ANDROID_MANIFEST); AdtPlugin.printErrorToConsole(project, msg); markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR); - + // This interrupts the build. The next builders will not run. stopBuild(msg); } - + // lets check the XML of the manifest first, if that hasn't been done by the // resource delta visitor yet. if (dv == null || dv.getCheckedManifestXml() == false) { BasicXmlErrorListener errorListener = new BasicXmlErrorListener(); AndroidManifestParser parser = BaseProjectHelper.parseManifestForError(manifest, errorListener); - + if (errorListener.mHasXmlError == true) { // there was an error in the manifest, its file has been marked, // by the XmlErrorHandler. @@ -305,25 +306,71 @@ public class PreCompilerBuilder extends BaseBuilder { String msg = String.format(Messages.s_Contains_Xml_Error, AndroidConstants.FN_ANDROID_MANIFEST); AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg); - + // This interrupts the build. The next builders will not run. stopBuild(msg); } - + // get the java package from the parser javaPackage = parser.getPackage(); minSdkVersion = parser.getApiLevelRequirement(); } - if (minSdkVersion != AndroidManifestParser.INVALID_MIN_SDK && - minSdkVersion < projectTarget.getApiVersionNumber()) { - // check it against the target api level - String msg = String.format( - "Manifest min SDK version (%1$d) is lower than project target API level (%2$d)", - minSdkVersion, projectTarget.getApiVersionNumber()); - AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg); - BaseProjectHelper.addMarker(manifest, AdtConstants.MARKER_ADT, msg, - IMarker.SEVERITY_WARNING); + if (minSdkVersion != null) { + int minSdkValue = -1; + try { + minSdkValue = Integer.parseInt(minSdkVersion); + } catch (NumberFormatException e) { + // it's ok, it means minSdkVersion contains a (hopefully) valid codename. + } + + AndroidVersion projectVersion = projectTarget.getVersion(); + + if (minSdkValue != -1) { + String codename = projectVersion.getCodename(); + if (codename != null) { + // integer minSdk when the target is a preview => fatal error + String msg = String.format( + "Platform %1$s is a preview and requires appication manifests to set %2$s to '%3$s'", + codename, AndroidManifestParser.ATTRIBUTE_MIN_SDK_VERSION, + codename); + AdtPlugin.printErrorToConsole(project, msg); + BaseProjectHelper.addMarker(manifest, AdtConstants.MARKER_ADT, msg, + IMarker.SEVERITY_ERROR); + stopBuild(msg); + } else if (minSdkValue < projectVersion.getApiLevel()) { + // integer minSdk is not high enough for the target => warning + String msg = String.format( + "Manifest min SDK version (%1$d) is lower than project target API level (%2$d)", + minSdkVersion, projectVersion.getApiLevel()); + AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg); + BaseProjectHelper.addMarker(manifest, AdtConstants.MARKER_ADT, msg, + IMarker.SEVERITY_WARNING); + } + } else { + // looks like the min sdk is a codename, check it matches the codename + // of the platform + String codename = projectVersion.getCodename(); + if (codename == null) { + // platform is not a preview => fatal error + String msg = String.format( + "Manifest attribute '%1$s' is set to '%2$s'. Integer is expected.", + AndroidManifestParser.ATTRIBUTE_MIN_SDK_VERSION, codename); + AdtPlugin.printErrorToConsole(project, msg); + BaseProjectHelper.addMarker(manifest, AdtConstants.MARKER_ADT, msg, + IMarker.SEVERITY_ERROR); + stopBuild(msg); + } else if (codename.equals(minSdkVersion) == false) { + // platform and manifest codenames don't match => fatal error. + String msg = String.format( + "Value of manifest attribute '%1$s' does not match platform codename '%2$s'", + AndroidManifestParser.ATTRIBUTE_MIN_SDK_VERSION, codename); + AdtPlugin.printErrorToConsole(project, msg); + BaseProjectHelper.addMarker(manifest, AdtConstants.MARKER_ADT, msg, + IMarker.SEVERITY_ERROR); + stopBuild(msg); + } + } } if (javaPackage == null || javaPackage.length() == 0) { @@ -332,11 +379,11 @@ public class PreCompilerBuilder extends BaseBuilder { AndroidConstants.FN_ANDROID_MANIFEST); AdtPlugin.printErrorToConsole(project, msg); markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR); - + // This interrupts the build. The next builders will not run. stopBuild(msg); } - + // at this point we have the java package. We need to make sure it's not a different // package than the previous one that were built. if (javaPackage.equals(mManifestPackage) == false) { @@ -345,64 +392,64 @@ public class PreCompilerBuilder extends BaseBuilder { if (mManifestPackage != null) { AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, Messages.Checking_Package_Change); - + FixLaunchConfig flc = new FixLaunchConfig(project, mManifestPackage, javaPackage); flc.start(); } - + // now we delete the generated classes from their previous location deleteObsoleteGeneratedClass(AndroidConstants.FN_RESOURCE_CLASS, mManifestPackage); deleteObsoleteGeneratedClass(AndroidConstants.FN_MANIFEST_CLASS, mManifestPackage); - + // record the new manifest package, and save it. mManifestPackage = javaPackage; saveProjectStringProperty(PROPERTY_PACKAGE, mManifestPackage); } - + if (mMustCompileResources) { // we need to figure out where to store the R class. // get the parent folder for R.java and update mManifestPackageSourceFolder IFolder packageFolder = getGenManifestPackageFolder(project); - + // get the resource folder IFolder resFolder = project.getFolder(AndroidConstants.WS_RESOURCES); - + // get the file system path IPath outputLocation = mGenFolder.getLocation(); IPath resLocation = resFolder.getLocation(); IPath manifestLocation = manifest.getLocation(); - + // those locations have to exist for us to do something! if (outputLocation != null && resLocation != null && manifestLocation != null) { String osOutputPath = outputLocation.toOSString(); String osResPath = resLocation.toOSString(); String osManifestPath = manifestLocation.toOSString(); - + // remove the aapt markers removeMarkersFromFile(manifest, AndroidConstants.MARKER_AAPT_COMPILE); removeMarkersFromContainer(resFolder, AndroidConstants.MARKER_AAPT_COMPILE); - + AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, Messages.Preparing_Generated_Files); - + // since the R.java file may be already existing in read-only // mode we need to make it readable so that aapt can overwrite // it IFile rJavaFile = packageFolder.getFile(AndroidConstants.FN_RESOURCE_CLASS); - + // do the same for the Manifest.java class IFile manifestJavaFile = packageFolder.getFile( AndroidConstants.FN_MANIFEST_CLASS); - + // we actually need to delete the manifest.java as it may become empty and // in this case aapt doesn't generate an empty one, but instead doesn't // touch it. manifestJavaFile.delete(true, null); - + // launch aapt: create the command line ArrayList<String> array = new ArrayList<String>(); array.add(projectTarget.getPath(IAndroidTarget.AAPT)); @@ -419,7 +466,7 @@ public class PreCompilerBuilder extends BaseBuilder { array.add(osResPath); array.add("-I"); //$NON-NLS-1$ array.add(projectTarget.getPath(IAndroidTarget.ANDROID_JAR)); - + if (AdtPlugin.getBuildVerbosity() == AdtConstants.BUILD_VERBOSE) { StringBuilder sb = new StringBuilder(); for (String c : array) { @@ -429,23 +476,23 @@ public class PreCompilerBuilder extends BaseBuilder { String cmd_line = sb.toString(); AdtPlugin.printToConsole(project, cmd_line); } - + // launch int execError = 1; try { // launch the command line process Process process = Runtime.getRuntime().exec( array.toArray(new String[array.size()])); - + // list to store each line of stderr ArrayList<String> results = new ArrayList<String>(); - + // get the output and return code from the process execError = grabProcessOutput(process, results); - + // attempt to parse the error output boolean parsingError = parseAaptOutput(results, project); - + // if we couldn't parse the output we display it in the console. if (parsingError) { if (execError != 0) { @@ -455,7 +502,7 @@ public class PreCompilerBuilder extends BaseBuilder { project, results.toArray()); } } - + if (execError != 0) { // if the exec failed, and we couldn't parse the error output // (and therefore not all files that should have been marked, @@ -464,10 +511,10 @@ public class PreCompilerBuilder extends BaseBuilder { markProject(AdtConstants.MARKER_ADT, Messages.Unparsed_AAPT_Errors, IMarker.SEVERITY_ERROR); } - + AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, Messages.AAPT_Error); - + // abort if exec failed. // This interrupts the build. The next builders will not run. stopBuild(Messages.AAPT_Error); @@ -477,7 +524,7 @@ public class PreCompilerBuilder extends BaseBuilder { // mark the project and exit String msg = String.format(Messages.AAPT_Exec_Error, array.get(0)); markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR); - + // This interrupts the build. The next builders will not run. stopBuild(msg); } catch (InterruptedException e) { @@ -485,11 +532,11 @@ public class PreCompilerBuilder extends BaseBuilder { // mark the project and exit String msg = String.format(Messages.AAPT_Exec_Error, array.get(0)); markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR); - + // This interrupts the build. The next builders will not run. stopBuild(msg); } - + // if the return code was OK, we refresh the folder that // contains R.java to force a java recompile. if (execError == 0) { @@ -497,10 +544,10 @@ public class PreCompilerBuilder extends BaseBuilder { // as derived. mDerivedProgressMonitor.addFile(rJavaFile); mDerivedProgressMonitor.addFile(manifestJavaFile); - + // build has been done. reset the state of the builder mMustCompileResources = false; - + // and store it saveProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES, mMustCompileResources); @@ -509,10 +556,10 @@ public class PreCompilerBuilder extends BaseBuilder { } else { // nothing to do } - + // now handle the aidl stuff. boolean aidlStatus = handleAidl(projectTarget, sourceFolderPathList, monitor); - + if (aidlStatus == false && mMustCompileResources == false) { AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, Messages.Nothing_To_Compile); @@ -540,14 +587,14 @@ public class PreCompilerBuilder extends BaseBuilder { @Override protected void startupOnInitialize() { super.startupOnInitialize(); - + mDerivedProgressMonitor = new DerivedProgressMonitor(); - + IProject project = getProject(); // load the previous IFolder and java package. mManifestPackage = loadProjectStringProperty(PROPERTY_PACKAGE); - + // get the source folder in which all the Java files are created mGenFolder = project.getFolder(SdkConstants.FD_GEN_SOURCES); @@ -555,14 +602,14 @@ public class PreCompilerBuilder extends BaseBuilder { // recompile. mMustCompileResources = loadProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES, true); boolean mustCompileAidl = loadProjectBooleanProperty(PROPERTY_COMPILE_AIDL, true); - + // if we stored that we have to compile some aidl, we build the list that will compile them // all if (mustCompileAidl) { IJavaProject javaProject = JavaCore.create(project); ArrayList<IPath> sourceFolderPathList = BaseProjectHelper.getSourceClasspaths( javaProject); - + buildAidlCompilationList(project, sourceFolderPathList); } } @@ -576,7 +623,7 @@ public class PreCompilerBuilder extends BaseBuilder { if (javaPackage == null) { return; } - + IPath packagePath = getJavaPackagePath(javaPackage); IPath iPath = packagePath.append(filename); @@ -614,10 +661,10 @@ public class PreCompilerBuilder extends BaseBuilder { path.append(AndroidConstants.WS_SEP_CHAR); path.append(s); } - + return new Path(path.toString()); } - + /** * Returns an {@link IFolder} (located inside the 'gen' source folder), that matches the * package defined in the manifest. This {@link IFolder} may not actually exist @@ -630,7 +677,7 @@ public class PreCompilerBuilder extends BaseBuilder { throws CoreException { // get the path for the package IPath packagePath = getJavaPackagePath(mManifestPackage); - + // get a folder for this path under the 'gen' source folder, and return it. // This IFolder may not reference an actual existing folder. return mGenFolder.getFolder(packagePath); @@ -657,10 +704,10 @@ public class PreCompilerBuilder extends BaseBuilder { command[index++] = projectTarget.getPath(IAndroidTarget.AIDL); command[index++] = "-p" + Sdk.getCurrent().getTarget(getProject()).getPath( //$NON-NLS-1$ IAndroidTarget.ANDROID_AIDL); - + // since the path are relative to the workspace and not the project itself, we need // the workspace root. - IWorkspaceRoot wsRoot = ResourcesPlugin.getWorkspace().getRoot(); + IWorkspaceRoot wsRoot = ResourcesPlugin.getWorkspace().getRoot(); for (IPath p : sourceFolders) { IFolder f = wsRoot.getFolder(p); command[index++] = "-I" + f.getLocation().toOSString(); //$NON-NLS-1$ @@ -686,7 +733,7 @@ public class PreCompilerBuilder extends BaseBuilder { // get the path of the source file. IPath sourcePath = aidlData.aidlFile.getLocation(); String osSourcePath = sourcePath.toOSString(); - + IFile javaFile = getGenDestinationFile(aidlData, true /*createFolders*/, monitor); // finish to set the command line. @@ -755,15 +802,15 @@ public class PreCompilerBuilder extends BaseBuilder { IPath packagePath = aidlData.aidlFile.getFullPath().removeFirstSegments( segmentToSourceFolderCount).removeLastSegments(1); Path destinationPath = new Path(packagePath.toString()); - + // get an IFolder for this path. It's relative to the 'gen' folder already IFolder destinationFolder = mGenFolder.getFolder(destinationPath); - + // create it if needed. if (destinationFolder.exists() == false && createFolders) { createFolder(destinationFolder, monitor); } - + // Build the Java file name from the aidl name. String javaName = aidlData.aidlFile.getName().replaceAll(AndroidConstants.RE_AIDL_EXT, AndroidConstants.DOT_JAVA); @@ -776,15 +823,15 @@ public class PreCompilerBuilder extends BaseBuilder { /** * Creates the destination folder. Because * {@link IFolder#create(boolean, boolean, IProgressMonitor)} only works if the parent folder - * already exists, this goes and ensure that all the parent folders actually exist, or it + * already exists, this goes and ensure that all the parent folders actually exist, or it * creates them as well. * @param destinationFolder The folder to create * @param monitor the {@link IProgressMonitor}, - * @throws CoreException + * @throws CoreException */ private void createFolder(IFolder destinationFolder, IProgressMonitor monitor) throws CoreException { - + // check the parent exist and create if necessary. IContainer parent = destinationFolder.getParent(); if (parent.getType() == IResource.FOLDER && parent.exists() == false) { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/PreCompilerDeltaVisitor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/PreCompilerDeltaVisitor.java index 38ff480..d5e6365 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/PreCompilerDeltaVisitor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/PreCompilerDeltaVisitor.java @@ -44,16 +44,16 @@ import java.util.ArrayList; * {@link PreCompilerBuilder}: * <ul><li>R.java/Manifest.java generated by compiling the resources</li> * <li>Any Java files generated by <code>aidl</code></li></ul>. - * + * * Therefore it looks for the following: * <ul><li>Any modification in the resource folder</li> * <li>Removed files from the source folder receiving generated Java files</li> * <li>Any modification to aidl files.</li> - * + * */ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements IResourceDeltaVisitor { - + private enum AidlType { UNKNOWN, INTERFACE, PARCELABLE; } @@ -73,7 +73,7 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements * into R.java */ private boolean mCompileResources = false; - + /** * Aidl force recompilation flag. If true, we'll attempt to recompile all aidl files. */ @@ -84,14 +84,14 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements /** List of .aidl files that have been removed. */ private final ArrayList<AidlData> mAidlToRemove = new ArrayList<AidlData>(); - + /** Manifest check/parsing flag. */ private boolean mCheckedManifestXml = false; /** Application Package, gathered from the parsing of the manifest */ private String mJavaPackage = null; /** minSDKVersion attribute value, gathered from the parsing of the manifest */ - private int mMinSdkVersion = AndroidManifestParser.INVALID_MIN_SDK; + private String mMinSdkVersion = null; // Internal usage fields. /** @@ -126,7 +126,7 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements public boolean getForceAidlCompile() { return mForceAidlCompile; } - + public ArrayList<AidlData> getAidlToCompile() { return mAidlToCompile; } @@ -134,7 +134,7 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements public ArrayList<AidlData> getAidlToRemove() { return mAidlToRemove; } - + /** * Returns whether the manifest file was parsed/checked for error during the resource delta * visiting. @@ -142,7 +142,7 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements public boolean getCheckedManifestXml() { return mCheckedManifestXml; } - + /** * Returns the manifest package if the manifest was checked/parsed. * <p/> @@ -162,16 +162,16 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements /** * Returns the minSDkVersion attribute from the manifest if it was checked/parsed. * <p/> - * This can return {@link AndroidManifestParser#INVALID_MIN_SDK} in two cases: + * This can return null in two cases: * <ul> * <li>The manifest was not part of the resource change delta, and the manifest was * not checked/parsed ({@link #getCheckedManifestXml()} returns <code>false</code>)</li> * <li>The manifest was parsed ({@link #getCheckedManifestXml()} returns <code>true</code>), * but the package declaration is missing</li> * </ul> - * @return the minSdkVersion or {@link AndroidManifestParser#INVALID_MIN_SDK}. + * @return the minSdkVersion or null. */ - public int getMinSdkVersion() { + public String getMinSdkVersion() { return mMinSdkVersion; } @@ -219,7 +219,7 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements // parse the manifest for errors AndroidManifestParser parser = BaseProjectHelper.parseManifestForError( (IFile)resource, this); - + if (parser != null) { mJavaPackage = parser.getPackage(); mMinSdkVersion = parser.getApiLevelRequirement(); @@ -287,19 +287,19 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements // Look for the source aidl file in all the source folders. String aidlFileName = fileName.replaceAll(AndroidConstants.RE_JAVA_EXT, AndroidConstants.DOT_AIDL); - + for (IPath sourceFolderPath : mSourceFolders) { // do not search in the current source folder as it is the 'gen' folder. if (sourceFolderPath.equals(mSourceFolder.getFullPath())) { continue; } - + IFolder sourceFolder = getFolder(sourceFolderPath); if (sourceFolder != null) { // go recursively, segment by segment. - // index starts at 2 (0 is project, 1 is 'gen' + // index starts at 2 (0 is project, 1 is 'gen' IFile sourceFile = findFile(sourceFolder, segments, 2, aidlFileName); - + if (sourceFile != null) { // found the source. add it to the list of files to compile mAidlToCompile.add(new AidlData(sourceFolder, sourceFile)); @@ -331,7 +331,7 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements if (AndroidConstants.EXT_AIDL.equalsIgnoreCase(ext)) { // first check whether it's a regular file or a parcelable. AidlType type = getAidlType(file); - + if (type == AidlType.INTERFACE) { if (kind == IResourceDelta.REMOVED) { // we'll have to remove the generated file. @@ -423,7 +423,7 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements path.segment(1).equals(SdkConstants.FD_GEN_SOURCES); return true; } - + // check if we are on the way to a source folder. int count = sourceFolderPath.matchingFirstSegments(path); if (count == path.segmentCount()) { @@ -443,7 +443,7 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements return false; } - + /** * Searches for and return a file in a folder. The file is defined by its segments, and a new * name (replacing the last segment). @@ -482,10 +482,10 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements if (resource != null && resource.exists() && resource.getType() == IResource.FOLDER) { return (IFolder)resource; } - + return null; } - + /** * Returns the type of the aidl file. Aidl files can either declare interfaces, or declare * parcelables. This method will attempt to parse the file and return the type. If the type diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/AndroidLaunchController.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/AndroidLaunchController.java index aad1812..b834bcf 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/AndroidLaunchController.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/AndroidLaunchController.java @@ -35,9 +35,9 @@ import com.android.ide.eclipse.adt.internal.project.ProjectHelper; import com.android.ide.eclipse.adt.internal.sdk.Sdk; import com.android.ide.eclipse.adt.internal.wizards.actions.AvdManagerAction; import com.android.prefs.AndroidLocation.AndroidLocationException; +import com.android.sdklib.AndroidVersion; import com.android.sdklib.IAndroidTarget; import com.android.sdklib.NullSdkLog; -import com.android.sdklib.SdkManager; import com.android.sdklib.internal.avd.AvdManager; import com.android.sdklib.internal.avd.AvdManager.AvdInfo; @@ -274,16 +274,16 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener * @param packageName the Android package name of the app * @param debugPackageName the Android package name to debug * @param debuggable the debuggable value of the app, or null if not set. - * @param requiredApiVersionNumber the api version required by the app, or - * {@link AndroidManifestParser#INVALID_MIN_SDK} if none. + * @param requiredApiVersionNumber the api version required by the app, or null if none. * @param launchAction the action to perform after app sync * @param config the launch configuration * @param launch the launch object */ public void launch(final IProject project, String mode, IFile apk, - String packageName, String debugPackageName, Boolean debuggable, int requiredApiVersionNumber, - final IAndroidLaunchAction launchAction, final AndroidLaunchConfiguration config, - final AndroidLaunch launch, IProgressMonitor monitor) { + String packageName, String debugPackageName, Boolean debuggable, + String requiredApiVersionNumber, final IAndroidLaunchAction launchAction, + final AndroidLaunchConfiguration config, final AndroidLaunch launch, + IProgressMonitor monitor) { String message = String.format("Performing %1$s", launchAction.getLaunchDescription()); AdtPlugin.printToConsole(project, message); @@ -398,17 +398,16 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener } else { if (projectTarget.isPlatform()) { // means this can run on any device as long // as api level is high enough - String apiString = d.getProperty(SdkManager.PROP_VERSION_SDK); - try { - int apiNumber = Integer.parseInt(apiString); - if (apiNumber >= projectTarget.getApiVersionNumber()) { - // device is compatible with project - compatibleRunningAvds.put(d, null); - continue; - } - } catch (NumberFormatException e) { - // do nothing, we'll consider it a non compatible device below. + AndroidVersion deviceVersion = Sdk.getDeviceVersion(d); + if (deviceVersion.canRun(projectTarget.getVersion())) { + // device is compatible with project + compatibleRunningAvds.put(d, null); + continue; } + } else { + // for non project platform, we can't be sure if a device can + // run an application or not, since we don't query the device + // for the list of optional libraries that it supports. } hasDevice = true; } @@ -544,9 +543,12 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener AvdInfo defaultAvd = null; for (AvdInfo avd : avds) { if (projectTarget.isCompatibleBaseFor(avd.getTarget())) { + // at this point we can ignore the code name issue since + // IAndroidTarget.isCompatibleBaseFor() will already have filtered the non + // compatible AVDs. if (defaultAvd == null || - avd.getTarget().getApiVersionNumber() < - defaultAvd.getTarget().getApiVersionNumber()) { + avd.getTarget().getVersion().getApiLevel() < + defaultAvd.getTarget().getVersion().getApiLevel()) { defaultAvd = avd; } } @@ -654,47 +656,67 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener if (device != null) { // check the app required API level versus the target device API level - String deviceApiVersionName = device.getProperty(IDevice.PROP_BUILD_VERSION); - String value = device.getProperty(IDevice.PROP_BUILD_VERSION_NUMBER); - int deviceApiVersionNumber = AndroidManifestParser.INVALID_MIN_SDK; + String deviceVersion = device.getProperty(IDevice.PROP_BUILD_VERSION); + String deviceApiLevelString = device.getProperty(IDevice.PROP_BUILD_API_LEVEL); + String deviceCodeName = device.getProperty(IDevice.PROP_BUILD_CODENAME); + + int deviceApiLevel = -1; try { - deviceApiVersionNumber = Integer.parseInt(value); + deviceApiLevel = Integer.parseInt(deviceApiLevelString); } catch (NumberFormatException e) { - // pass, we'll keep the deviceVersionNumber value at 0. + // pass, we'll keep the apiLevel value at -1. } - if (launchInfo.getRequiredApiVersionNumber() == AndroidManifestParser.INVALID_MIN_SDK) { - // warn the API level requirement is not set. + String requiredApiString = launchInfo.getRequiredApiVersionNumber(); + if (requiredApiString != null) { + int requiredApi = -1; + try { + requiredApi = Integer.parseInt(requiredApiString); + } catch (NumberFormatException e) { + // pass, we'll keep requiredApi value at -1. + } + + if (requiredApi == -1) { + // this means the manifest uses a codename for minSdkVersion + // check that the device is using the same codename + if (requiredApiString.equals(deviceCodeName) == false) { + AdtPlugin.printErrorToConsole(launchInfo.getProject(), String.format( + "ERROR: Application requires a device running '%1$s'!", + requiredApiString)); + return false; + } + } else { + // app requires a specific API level + if (deviceApiLevel == -1) { + AdtPlugin.printToConsole(launchInfo.getProject(), + "WARNING: Unknown device API version!"); + } else if (deviceApiLevel < requiredApi) { + String msg = String.format( + "ERROR: Application requires API version %1$d. Device API version is %2$d (Android %3$s).", + requiredApi, deviceApiLevel, deviceVersion); + AdtPlugin.printErrorToConsole(launchInfo.getProject(), msg); + + // abort the launch + return false; + } + } + } else { + // warn the application API level requirement is not set. AdtPlugin.printErrorToConsole(launchInfo.getProject(), "WARNING: Application does not specify an API level requirement!"); // and display the target device API level (if known) - if (deviceApiVersionName == null || - deviceApiVersionNumber == AndroidManifestParser.INVALID_MIN_SDK) { + if (deviceApiLevel == -1) { AdtPlugin.printErrorToConsole(launchInfo.getProject(), "WARNING: Unknown device API version!"); } else { AdtPlugin.printErrorToConsole(launchInfo.getProject(), String.format( - "Device API version is %1$d (Android %2$s)", deviceApiVersionNumber, - deviceApiVersionName)); - } - } else { // app requires a specific API level - if (deviceApiVersionName == null || - deviceApiVersionNumber == AndroidManifestParser.INVALID_MIN_SDK) { - AdtPlugin.printToConsole(launchInfo.getProject(), - "WARNING: Unknown device API version!"); - } else if (deviceApiVersionNumber < launchInfo.getRequiredApiVersionNumber()) { - String msg = String.format( - "ERROR: Application requires API version %1$d. Device API version is %2$d (Android %3$s).", - launchInfo.getRequiredApiVersionNumber(), deviceApiVersionNumber, - deviceApiVersionName); - AdtPlugin.printErrorToConsole(launchInfo.getProject(), msg); - - // abort the launch - return false; + "Device API version is %1$d (Android %2$s)", deviceApiLevel, + deviceVersion)); } } + // now checks that the device/app can be debugged (if needed) if (device.isEmulator() == false && launchInfo.isDebugMode()) { String debuggableDevice = device.getProperty(IDevice.PROP_DEBUGGABLE); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/DelayedLaunchInfo.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/DelayedLaunchInfo.java index aaa8e89..01c94fe 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/DelayedLaunchInfo.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/DelayedLaunchInfo.java @@ -17,7 +17,6 @@ package com.android.ide.eclipse.adt.internal.launch; import com.android.ddmlib.IDevice; -import com.android.ide.eclipse.adt.internal.project.AndroidManifestParser; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; @@ -28,14 +27,14 @@ import org.eclipse.core.runtime.IProgressMonitor; * application is launched. */ public final class DelayedLaunchInfo { - + /** - * Used to indicate behavior when Android app already exists + * Used to indicate behavior when Android app already exists */ enum InstallRetryMode { - NEVER, ALWAYS, PROMPT; + NEVER, ALWAYS, PROMPT; } - + /** The device on which to launch the app */ private IDevice mDevice = null; @@ -44,22 +43,21 @@ public final class DelayedLaunchInfo { /** Package name */ private final String mPackageName; - + /** Debug package name */ private final String mDebugPackageName; /** IFile to the package (.apk) file */ private final IFile mPackageFile; - + /** debuggable attribute of the manifest file. */ private final Boolean mDebuggable; - - /** Required ApiVersionNumber by the app. {@link AndroidManifestParser#INVALID_MIN_SDK} means - * no requirements */ - private final int mRequiredApiVersionNumber; - + + /** Required Api level by the app. null means no requirements */ + private final String mRequiredApiVersionNumber; + private InstallRetryMode mRetryMode = InstallRetryMode.NEVER; - + /** Launch action. */ private final IAndroidLaunchAction mLaunchAction; @@ -78,23 +76,22 @@ public final class DelayedLaunchInfo { /** cancellation state of launch */ private boolean mCancelled = false; - /** - * Basic constructor with activity and package info. - * + /** + * Basic constructor with activity and package info. + * * @param project the eclipse project that corresponds to Android app * @param packageName package name of Android app * @param debugPackageName the package name of the Andriod app to debug * @param launchAction action to perform after app install * @param pack IFile to the package (.apk) file * @param debuggable debuggable attribute of the app's manifest file. - * @param requiredApiVersionNumber required SDK version by the app. - * {@link AndroidManifestParser#INVALID_MIN_SDK} means no requirements. + * @param requiredApiVersionNumber required SDK version by the app. null means no requirements. * @param launch the launch object * @param monitor progress monitor for launch */ public DelayedLaunchInfo(IProject project, String packageName, String debugPackageName, - IAndroidLaunchAction launchAction, IFile pack, Boolean debuggable, - int requiredApiVersionNumber, AndroidLaunch launch, IProgressMonitor monitor) { + IAndroidLaunchAction launchAction, IFile pack, Boolean debuggable, + String requiredApiVersionNumber, AndroidLaunch launch, IProgressMonitor monitor) { mProject = project; mPackageName = packageName; mDebugPackageName = debugPackageName; @@ -112,7 +109,7 @@ public final class DelayedLaunchInfo { public IDevice getDevice() { return mDevice; } - + /** * Set the device on which to launch the app */ @@ -153,16 +150,16 @@ public final class DelayedLaunchInfo { } /** - * @return true if Android app is marked as debuggable in its manifest + * @return true if Android app is marked as debuggable in its manifest */ public Boolean getDebuggable() { return mDebuggable; } /** - * @return the required api version number for the Android app + * @return the required api version number for the Android app. */ - public int getRequiredApiVersionNumber() { + public String getRequiredApiVersionNumber() { return mRequiredApiVersionNumber; } @@ -195,7 +192,7 @@ public final class DelayedLaunchInfo { } /** - * @return the launch progress monitor + * @return the launch progress monitor */ public IProgressMonitor getMonitor() { return mMonitor; @@ -230,7 +227,7 @@ public final class DelayedLaunchInfo { } /** - * Set if launch has been cancelled + * Set if launch has been cancelled */ public void setCancelled(boolean cancelled) { this.mCancelled = cancelled; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/DeviceChooserDialog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/DeviceChooserDialog.java index d2fe5ae..8b61e27 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/DeviceChooserDialog.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/DeviceChooserDialog.java @@ -27,6 +27,7 @@ import com.android.ddmuilib.TableHelper; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.internal.sdk.Sdk; import com.android.ide.eclipse.ddms.DdmsPlugin; +import com.android.sdklib.AndroidVersion; import com.android.sdklib.IAndroidTarget; import com.android.sdklib.internal.avd.AvdManager.AvdInfo; import com.android.sdkuilib.internal.widgets.AvdSelector; @@ -131,26 +132,19 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener case 2: // check for compatibility. if (device.isEmulator() == false) { // physical device - // get the api level of the device - try { - String apiValue = device.getProperty( - IDevice.PROP_BUILD_VERSION_NUMBER); - if (apiValue != null) { - int api = Integer.parseInt(apiValue); - if (api >= mProjectTarget.getApiVersionNumber()) { - // if the project is compiling against an add-on, the optional - // API may be missing from the device. - return mProjectTarget.isPlatform() ? - mMatchImage : mWarningImage; - } else { - return mNoMatchImage; - } - } else { - return mWarningImage; + // get the version of the device + AndroidVersion deviceVersion = Sdk.getDeviceVersion(device); + if (deviceVersion == null) { + return mWarningImage; + } else { + if (deviceVersion.canRun(mProjectTarget.getVersion()) == false) { + return mNoMatchImage; } - } catch (NumberFormatException e) { - // lets consider the device non compatible - return mNoMatchImage; + + // if the project is compiling against an add-on, + // the optional API may be missing from the device. + return mProjectTarget.isPlatform() ? + mMatchImage : mWarningImage; } } else { // get the AvdInfo diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidManifestParser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidManifestParser.java index 3deea23..d5fd4e7 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidManifestParser.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidManifestParser.java @@ -51,7 +51,7 @@ public class AndroidManifestParser { private final static String ATTRIBUTE_NAME = "name"; //$NON-NLS-1$ private final static String ATTRIBUTE_PROCESS = "process"; //$NON-NLS-$ private final static String ATTRIBUTE_DEBUGGABLE = "debuggable"; //$NON-NLS-$ - private final static String ATTRIBUTE_MIN_SDK_VERSION = "minSdkVersion"; //$NON-NLS-$ + public final static String ATTRIBUTE_MIN_SDK_VERSION = "minSdkVersion"; //$NON-NLS-$ private final static String ATTRIBUTE_TARGET_PACKAGE = "targetPackage"; //$NON-NLS-1$ private final static String ATTRIBUTE_EXPORTED = "exported"; //$NON-NLS-1$ private final static String NODE_MANIFEST = "manifest"; //$NON-NLS-1$ @@ -76,8 +76,6 @@ public class AndroidManifestParser { private final static String ACTION_MAIN = "android.intent.action.MAIN"; //$NON-NLS-1$ private final static String CATEGORY_LAUNCHER = "android.intent.category.LAUNCHER"; //$NON-NLS-1$ - public final static int INVALID_MIN_SDK = -1; - /** * Instrumentation info obtained from manifest */ @@ -179,9 +177,8 @@ public class AndroidManifestParser { private Set<String> mProcesses = null; /** debuggable attribute value. If null, the attribute is not present. */ private Boolean mDebuggable = null; - /** API level requirement. if {@link AndroidManifestParser#INVALID_MIN_SDK} - * the attribute was not present. */ - private int mApiLevelRequirement = INVALID_MIN_SDK; + /** API level requirement. if null the attribute was not present. */ + private String mApiLevelRequirement = null; /** List of all instrumentations declared by the manifest */ private final ArrayList<Instrumentation> mInstrumentations = new ArrayList<Instrumentation>(); @@ -258,10 +255,9 @@ public class AndroidManifestParser { } /** - * Returns the <code>minSdkVersion</code> attribute, or - * {@link AndroidManifestParser#INVALID_MIN_SDK} if it's not set. + * Returns the <code>minSdkVersion</code> attribute, or null if it's not set. */ - int getApiLevelRequirement() { + String getApiLevelRequirement() { return mApiLevelRequirement; } @@ -331,16 +327,8 @@ public class AndroidManifestParser { mValidLevel++; } else if (NODE_USES_SDK.equals(localName)) { - value = getAttributeValue(attributes, ATTRIBUTE_MIN_SDK_VERSION, - true /* hasNamespace */); - - if (value != null) { - try { - mApiLevelRequirement = Integer.parseInt(value); - } catch (NumberFormatException e) { - handleError(e, -1 /* lineNumber */); - } - } + mApiLevelRequirement = getAttributeValue(attributes, + ATTRIBUTE_MIN_SDK_VERSION, true /* hasNamespace */); } else if (NODE_INSTRUMENTATION.equals(localName)) { processInstrumentationNode(attributes); } @@ -636,7 +624,7 @@ public class AndroidManifestParser { private final Activity mLauncherActivity; private final String[] mProcesses; private final Boolean mDebuggable; - private final int mApiLevelRequirement; + private final String mApiLevelRequirement; private final Instrumentation[] mInstrumentations; private final String[] mLibraries; @@ -904,10 +892,9 @@ public class AndroidManifestParser { } /** - * Returns the <code>minSdkVersion</code> attribute, or {@link #INVALID_MIN_SDK} - * if it's not set. + * Returns the <code>minSdkVersion</code> attribute, or null if it's not set. */ - public int getApiLevelRequirement() { + public String getApiLevelRequirement() { return mApiLevelRequirement; } @@ -939,13 +926,13 @@ public class AndroidManifestParser { * @param launcherActivity the launcher activity parser from the manifest. * @param processes the list of custom processes declared in the manifest. * @param debuggable the debuggable attribute, or null if not set. - * @param apiLevelRequirement the minSdkVersion attribute value or 0 if not set. + * @param apiLevelRequirement the minSdkVersion attribute value or null if not set. * @param instrumentations the list of instrumentations parsed from the manifest. * @param libraries the list of libraries in use parsed from the manifest. */ private AndroidManifestParser(String javaPackage, Activity[] activities, Activity launcherActivity, String[] processes, Boolean debuggable, - int apiLevelRequirement, Instrumentation[] instrumentations, String[] libraries) { + String apiLevelRequirement, Instrumentation[] instrumentations, String[] libraries) { mJavaPackage = javaPackage; mActivities = activities; mLauncherActivity = launcherActivity; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetParser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetParser.java index 5c468ec..a499137 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetParser.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetParser.java @@ -66,10 +66,10 @@ import javax.management.InvalidAttributeValueException; * <li>Resource ID from <code>android.R</code></li> * <li>The list of permissions values from <code>android.Manifest$permission</code></li> * <li></li> - * </ul> + * </ul> */ public final class AndroidTargetParser { - + private static final String TAG = "Framework Resource Parser"; private final IAndroidTarget mAndroidTarget; @@ -79,11 +79,11 @@ public final class AndroidTargetParser { public AndroidTargetParser(IAndroidTarget platformTarget) { mAndroidTarget = platformTarget; } - + /** * Parses the framework, collects all interesting information and stores them in the * {@link IAndroidTarget} given to the constructor. - * + * * @param monitor A progress monitor. Can be null. Caller is responsible for calling done. * @return True if the SDK path was valid and parsing has been attempted. */ @@ -92,7 +92,7 @@ public final class AndroidTargetParser { SubMonitor progress = SubMonitor.convert(monitor, String.format("Parsing SDK %1$s", mAndroidTarget.getName()), 14); - + AndroidTargetData targetData = new AndroidTargetData(mAndroidTarget); // load DX. @@ -103,22 +103,22 @@ public final class AndroidTargetParser { String.format("dx.jar loading failed for target '%1$s'", mAndroidTarget.getFullName())); } - + // we have loaded dx. targetData.setDexWrapper(dexWrapper); progress.worked(1); - + // parse the rest of the data. AndroidJarLoader classLoader = new AndroidJarLoader(mAndroidTarget.getPath(IAndroidTarget.ANDROID_JAR)); - + preload(classLoader, progress.newChild(40, SubMonitor.SUPPRESS_NONE)); - + if (progress.isCanceled()) { return Status.CANCEL_STATUS; } - + // get the resource Ids. progress.subTask("Resource IDs"); IResourceRepository frameworkRepository = collectResourceIds(classLoader); @@ -172,7 +172,7 @@ public final class AndroidTargetParser { progress.subTask("Widgets and layouts"); collectLayoutClasses(classLoader, attrsXmlParser, mainList, groupList, progress.newChild(1)); - + if (progress.isCanceled()) { return Status.CANCEL_STATUS; } @@ -180,7 +180,7 @@ public final class AndroidTargetParser { ViewClassInfo[] layoutViewsInfo = mainList.toArray(new ViewClassInfo[mainList.size()]); ViewClassInfo[] layoutGroupsInfo = groupList.toArray( new ViewClassInfo[groupList.size()]); - + // collect the preferences classes. mainList.clear(); groupList.clear(); @@ -203,17 +203,17 @@ public final class AndroidTargetParser { Map<String, Map<String, Integer>> enumValueMap = attrsXmlParser.getEnumFlagValues(); Map<String, DeclareStyleableInfo> xmlAppWidgetMap = null; - if (mAndroidTarget.getApiVersionNumber() >= 3) { + if (mAndroidTarget.getVersion().getApiLevel() >= 3) { xmlAppWidgetMap = collectAppWidgetDefinitions(attrsXmlParser); } if (progress.isCanceled()) { return Status.CANCEL_STATUS; } - + // From the information that was collected, create the pieces that will be put in // the PlatformData object. - AndroidManifestDescriptors manifestDescriptors = new AndroidManifestDescriptors(); + AndroidManifestDescriptors manifestDescriptors = new AndroidManifestDescriptors(); manifestDescriptors.updateDescriptors(manifestMap); progress.worked(1); @@ -244,16 +244,16 @@ public final class AndroidTargetParser { preferencesInfo, preferenceGroupsInfo); progress.worked(1); - + // load the framework resources. ProjectResources resources = ResourceManager.getInstance().loadFrameworkResources( mAndroidTarget); progress.worked(1); - + // now load the layout lib bridge LayoutBridge layoutBridge = loadLayoutBridge(); progress.worked(1); - + // and finally create the PlatformData with all that we loaded. targetData.setExtraData(frameworkRepository, manifestDescriptors, @@ -270,7 +270,7 @@ public final class AndroidTargetParser { mAndroidTarget.getOptionalLibraries(), resources, layoutBridge); - + Sdk.getCurrent().setTargetData(mAndroidTarget, targetData); return Status.OK_STATUS; @@ -285,7 +285,7 @@ public final class AndroidTargetParser { * Preloads all "interesting" classes from the framework SDK jar. * <p/> * Currently this preloads all classes from the framework jar - * + * * @param classLoader The framework SDK jar classloader * @param monitor A progress monitor. Can be null. Caller is responsible for calling done. */ @@ -303,7 +303,7 @@ public final class AndroidTargetParser { /** * Creates an IResourceRepository for the framework resources. - * + * * @param classLoader The framework SDK jar classloader * @return a map of the resources, or null if it failed. */ @@ -311,7 +311,7 @@ public final class AndroidTargetParser { AndroidJarLoader classLoader) { try { Class<?> r = classLoader.loadClass(AndroidConstants.CLASS_R); - + if (r != null) { Map<ResourceType, List<ResourceItem>> map = parseRClass(r); if (map != null) { @@ -321,23 +321,23 @@ public final class AndroidTargetParser { } catch (ClassNotFoundException e) { AdtPlugin.logAndPrintError(e, TAG, "Collect resource IDs failed, class %1$s not found in %2$s", //$NON-NLS-1$ - AndroidConstants.CLASS_R, + AndroidConstants.CLASS_R, mAndroidTarget.getPath(IAndroidTarget.ANDROID_JAR)); } - + return null; } - + /** * Parse the R class and build the resource map. - * + * * @param rClass the Class object representing the Resources. * @return a map of the resource or null */ private Map<ResourceType, List<ResourceItem>> parseRClass(Class<?> rClass) { // get the sub classes. Class<?>[] classes = rClass.getClasses(); - + if (classes.length > 0) { HashMap<ResourceType, List<ResourceItem>> map = new HashMap<ResourceType, List<ResourceItem>>(); @@ -346,30 +346,30 @@ public final class AndroidTargetParser { for (int c = 0 ; c < classes.length ; c++) { Class<?> subClass = classes[c]; String name = subClass.getSimpleName(); - + // get the matching ResourceType ResourceType type = ResourceType.getEnum(name); if (type != null) { List<ResourceItem> list = new ArrayList<ResourceItem>(); map.put(type, list); - + Field[] fields = subClass.getFields(); - + for (Field f : fields) { list.add(new ResourceItem(f.getName())); } } } - + return map; } - + return null; } /** * Loads, collects and returns the list of default permissions from the framework. - * + * * @param classLoader The framework SDK jar classloader * @return a non null (but possibly empty) array containing the permission values. */ @@ -377,12 +377,12 @@ public final class AndroidTargetParser { try { Class<?> permissionClass = classLoader.loadClass(AndroidConstants.CLASS_MANIFEST_PERMISSION); - + if (permissionClass != null) { ArrayList<String> list = new ArrayList<String>(); Field[] fields = permissionClass.getFields(); - + for (Field f : fields) { int modifiers = f.getModifiers(); if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers) && @@ -403,23 +403,23 @@ public final class AndroidTargetParser { } } } - + return list.toArray(new String[list.size()]); } } catch (ClassNotFoundException e) { AdtPlugin.logAndPrintError(e, TAG, "Collect permissions failed, class %1$s not found in %2$s", //$NON-NLS-1$ - AndroidConstants.CLASS_MANIFEST_PERMISSION, + AndroidConstants.CLASS_MANIFEST_PERMISSION, mAndroidTarget.getPath(IAndroidTarget.ANDROID_JAR)); } - + return new String[0]; } - + /** * Loads and collects the action and category default values from the framework. * The values are added to the <code>actions</code> and <code>categories</code> lists. - * + * * @param activityActions the list which will receive the activity action values. * @param broadcastActions the list which will receive the broadcast action values. * @param serviceActions the list which will receive the service action values. @@ -481,7 +481,7 @@ public final class AndroidTargetParser { /** * Collects all layout classes information from the class loader and the * attrs.xml and sets the corresponding structures in the resource manager. - * + * * @param classLoader The framework SDK jar classloader in case we cannot get the widget from * the platform directly * @param attrsXmlParser The parser of the attrs.xml file @@ -491,7 +491,7 @@ public final class AndroidTargetParser { */ private void collectLayoutClasses(AndroidJarLoader classLoader, AttrsXmlParser attrsXmlParser, - Collection<ViewClassInfo> mainList, Collection<ViewClassInfo> groupList, + Collection<ViewClassInfo> mainList, Collection<ViewClassInfo> groupList, IProgressMonitor monitor) { LayoutParamsParser ldp = null; try { @@ -510,7 +510,7 @@ public final class AndroidTargetParser { ldp = new LayoutParamsParser(classLoader, attrsXmlParser); } ldp.parseLayoutClasses(monitor); - + List<ViewClassInfo> views = ldp.getViews(); List<ViewClassInfo> groups = ldp.getGroups(); @@ -523,7 +523,7 @@ public final class AndroidTargetParser { /** * Collects all preferences definition information from the attrs.xml and * sets the corresponding structures in the resource manager. - * + * * @param classLoader The framework SDK jar classloader * @param attrsXmlParser The parser of the attrs.xml file * @param mainList the Collection to receive the main list of {@link ViewClassInfo}. @@ -534,13 +534,13 @@ public final class AndroidTargetParser { AttrsXmlParser attrsXmlParser, Collection<ViewClassInfo> mainList, Collection<ViewClassInfo> groupList, IProgressMonitor monitor) { LayoutParamsParser ldp = new LayoutParamsParser(classLoader, attrsXmlParser); - + try { ldp.parsePreferencesClasses(monitor); - + List<ViewClassInfo> prefs = ldp.getViews(); List<ViewClassInfo> groups = ldp.getGroups(); - + if (prefs != null && groups != null) { mainList.addAll(prefs); groupList.addAll(groups); @@ -548,7 +548,7 @@ public final class AndroidTargetParser { } catch (NoClassDefFoundError e) { AdtPlugin.logAndPrintError(e, TAG, "Collect preferences failed, class %1$s not found in %2$s", - e.getMessage(), + e.getMessage(), classLoader.getSource()); } catch (Throwable e) { AdtPlugin.log(e, "Android Framework Parser: failed to collect preference classes"); //$NON-NLS-1$ @@ -559,7 +559,7 @@ public final class AndroidTargetParser { /** * Collects all menu definition information from the attrs.xml and returns it. - * + * * @param attrsXmlParser The parser of the attrs.xml file */ private Map<String, DeclareStyleableInfo> collectMenuDefinitions( @@ -575,18 +575,18 @@ public final class AndroidTargetParser { AdtPlugin.log(IStatus.WARNING, "Menu declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$ key, attrsXmlParser.getOsAttrsXmlPath()); - AdtPlugin.printErrorToConsole("Android Framework Parser", + AdtPlugin.printErrorToConsole("Android Framework Parser", String.format("Menu declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$ key, attrsXmlParser.getOsAttrsXmlPath())); } } - + return Collections.unmodifiableMap(map2); } /** * Collects all searchable definition information from the attrs.xml and returns it. - * + * * @param attrsXmlParser The parser of the attrs.xml file */ private Map<String, DeclareStyleableInfo> collectSearchableDefinitions( @@ -612,7 +612,7 @@ public final class AndroidTargetParser { /** * Collects all appWidgetProviderInfo definition information from the attrs.xml and returns it. - * + * * @param attrsXmlParser The parser of the attrs.xml file */ private Map<String, DeclareStyleableInfo> collectAppWidgetDefinitions( @@ -657,13 +657,13 @@ public final class AndroidTargetParser { AdtPlugin.log(IStatus.ERROR, "layoutlib.jar is missing!"); //$NON-NLS-1$ } else { URL url = f.toURL(); - + // create a class loader. Because this jar reference interfaces - // that are in the editors plugin, it's important to provide + // that are in the editors plugin, it's important to provide // a parent class loader. layoutBridge.classLoader = new URLClassLoader(new URL[] { url }, this.getClass().getClassLoader()); - + // load the class Class<?> clazz = layoutBridge.classLoader.loadClass(AndroidConstants.CLASS_BRIDGE); if (clazz != null) { @@ -676,7 +676,7 @@ public final class AndroidTargetParser { } } } - + if (layoutBridge.bridge == null) { layoutBridge.status = LoadStatus.FAILED; AdtPlugin.log(IStatus.ERROR, "Failed to load " + AndroidConstants.CLASS_BRIDGE); //$NON-NLS-1$ @@ -688,7 +688,7 @@ public final class AndroidTargetParser { // the first version of the api did not have this method layoutBridge.apiLevel = 1; } - + // and mark the lib as loaded. layoutBridge.status = LoadStatus.LOADED; } @@ -698,7 +698,7 @@ public final class AndroidTargetParser { // log the error. AdtPlugin.log(t, "Failed to load the LayoutLib"); } - + return layoutBridge; } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java index 0fe592a..d6cfeae 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java @@ -16,12 +16,14 @@ package com.android.ide.eclipse.adt.internal.sdk; +import com.android.ddmlib.IDevice; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.internal.project.AndroidClasspathContainerInitializer; import com.android.ide.eclipse.adt.internal.resources.manager.ResourceMonitor; import com.android.ide.eclipse.adt.internal.resources.manager.ResourceMonitor.IProjectListener; import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData.LayoutBridge; import com.android.prefs.AndroidLocation.AndroidLocationException; +import com.android.sdklib.AndroidVersion; import com.android.sdklib.IAndroidTarget; import com.android.sdklib.ISdkLog; import com.android.sdklib.SdkConstants; @@ -394,6 +396,21 @@ public class Sdk implements IProjectListener { return mAvdManager; } + public static AndroidVersion getDeviceVersion(IDevice device) { + try { + Map<String, String> props = device.getProperties(); + String apiLevel = props.get(IDevice.PROP_BUILD_API_LEVEL); + if (apiLevel == null) { + return null; + } + + return new AndroidVersion(Integer.parseInt(apiLevel), + props.get((IDevice.PROP_BUILD_CODENAME))); + } catch (NumberFormatException e) { + return null; + } + } + private Sdk(SdkManager manager, AvdManager avdManager) { mManager = manager; mAvdManager = avdManager; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectCreationPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectCreationPage.java index 95b6ea1..5067111 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectCreationPage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectCreationPage.java @@ -840,25 +840,21 @@ public class NewProjectCreationPage extends WizardPage { return; } - try { - int version = Integer.parseInt(mInfo.getMinSdkVersion()); - - // Before changing, compare with the currently selected one, if any. - // There can be multiple targets with the same sdk api version, so don't change - // it if it's already at the right version. - IAndroidTarget curr_target = mInfo.getSdkTarget(); - if (curr_target != null && curr_target.getApiVersionNumber() == version) { - return; - } + String minSdkVersion = mInfo.getMinSdkVersion(); - for (IAndroidTarget target : mSdkTargetSelector.getTargets()) { - if (target.getApiVersionNumber() == version) { - mSdkTargetSelector.setSelection(target); - break; - } + // Before changing, compare with the currently selected one, if any. + // There can be multiple targets with the same sdk api version, so don't change + // it if it's already at the right version. + IAndroidTarget curr_target = mInfo.getSdkTarget(); + if (curr_target != null && curr_target.getVersion().equals(minSdkVersion)) { + return; + } + + for (IAndroidTarget target : mSdkTargetSelector.getTargets()) { + if (target.getVersion().equals(minSdkVersion)) { + mSdkTargetSelector.setSelection(target); + break; } - } catch (NumberFormatException e) { - // ignore } } @@ -873,7 +869,7 @@ public class NewProjectCreationPage extends WizardPage { if (target != null) { mInternalMinSdkVersionUpdate = true; - mMinSdkVersionField.setText(Integer.toString(target.getApiVersionNumber())); + mMinSdkVersionField.setText(target.getVersion().getApiString()); mInternalMinSdkVersionUpdate = false; } } @@ -932,7 +928,7 @@ public class NewProjectCreationPage extends WizardPage { String packageName = null; Activity activity = null; String activityName = null; - int minSdkVersion = AndroidManifestParser.INVALID_MIN_SDK; + String minSdkVersion = null; try { packageName = manifestData.getPackage(); minSdkVersion = manifestData.getApiLevelRequirement(); @@ -1033,17 +1029,13 @@ public class NewProjectCreationPage extends WizardPage { } } - if (!foundTarget && minSdkVersion != AndroidManifestParser.INVALID_MIN_SDK) { - try { - for (IAndroidTarget target : mSdkTargetSelector.getTargets()) { - if (target.getApiVersionNumber() == minSdkVersion) { - mSdkTargetSelector.setSelection(target); - foundTarget = true; - break; - } + if (!foundTarget && minSdkVersion != null) { + for (IAndroidTarget target : mSdkTargetSelector.getTargets()) { + if (target.getVersion().equals(minSdkVersion)) { + mSdkTargetSelector.setSelection(target); + foundTarget = true; + break; } - } catch(NumberFormatException e) { - // ignore } } @@ -1059,9 +1051,9 @@ public class NewProjectCreationPage extends WizardPage { if (!foundTarget) { mInternalMinSdkVersionUpdate = true; - mMinSdkVersionField.setText( - minSdkVersion == AndroidManifestParser.INVALID_MIN_SDK ? "" //$NON-NLS-1$ - : Integer.toString(minSdkVersion)); + if (minSdkVersion != null) { + mMinSdkVersionField.setText(minSdkVersion); + } mInternalMinSdkVersionUpdate = false; } } @@ -1269,21 +1261,10 @@ public class NewProjectCreationPage extends WizardPage { return MSG_NONE; } - int version = AndroidManifestParser.INVALID_MIN_SDK; - try { - // If not empty, it must be a valid integer > 0 - version = Integer.parseInt(mInfo.getMinSdkVersion()); - } catch (NumberFormatException e) { - // ignore - } - - if (version < 1) { - return setStatus("Min SDK Version must be an integer > 0.", MSG_ERROR); - } - - if (mInfo.getSdkTarget() != null && mInfo.getSdkTarget().getApiVersionNumber() != version) { + if (mInfo.getSdkTarget() != null && + mInfo.getSdkTarget().getVersion().equals(mInfo.getMinSdkVersion()) == false) { return setStatus("The API level for the selected SDK target does not match the Min SDK version.", - MSG_WARNING); + mInfo.getSdkTarget().getVersion().isPreview() ? MSG_ERROR : MSG_WARNING); } return MSG_NONE; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewTestProjectCreationPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewTestProjectCreationPage.java index 22c2749..94fd99d 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewTestProjectCreationPage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewTestProjectCreationPage.java @@ -821,7 +821,7 @@ public class NewTestProjectCreationPage extends WizardPage { if (manifestData != null) { String appName = String.format("%1$sTest", project.getName()); String packageName = manifestData.getPackage(); - int minSdkVersion = manifestData.getApiLevelRequirement(); + String minSdkVersion = manifestData.getApiLevelRequirement(); IAndroidTarget sdkTarget = null; if (Sdk.getCurrent() != null) { sdkTarget = Sdk.getCurrent().getTarget(project); @@ -859,9 +859,9 @@ public class NewTestProjectCreationPage extends WizardPage { if (!mMinSdkVersionModifiedByUser) { mInternalMinSdkVersionUpdate = true; - mMinSdkVersionField.setText( - minSdkVersion != AndroidManifestParser.INVALID_MIN_SDK ? - Integer.toString(minSdkVersion) : ""); //$NON-NLS-1$ + if (minSdkVersion != null) { + mMinSdkVersionField.setText(minSdkVersion); + } if (sdkTarget == null) { updateSdkSelectorToMatchMinSdkVersion(); } @@ -1052,22 +1052,18 @@ public class NewTestProjectCreationPage extends WizardPage { * that matches. */ private void updateSdkSelectorToMatchMinSdkVersion() { - try { - int version = Integer.parseInt(mInfo.getMinSdkVersion()); + String minSdkVersion = mInfo.getMinSdkVersion(); - IAndroidTarget curr_target = mInfo.getSdkTarget(); - if (curr_target != null && curr_target.getApiVersionNumber() == version) { - return; - } + IAndroidTarget curr_target = mInfo.getSdkTarget(); + if (curr_target != null && curr_target.getVersion().equals(minSdkVersion)) { + return; + } - for (IAndroidTarget target : mSdkTargetSelector.getTargets()) { - if (target.getApiVersionNumber() == version) { - mSdkTargetSelector.setSelection(target); - return; - } + for (IAndroidTarget target : mSdkTargetSelector.getTargets()) { + if (target.getVersion().equals(minSdkVersion)) { + mSdkTargetSelector.setSelection(target); + return; } - } catch (NumberFormatException e) { - // ignore } } @@ -1086,7 +1082,7 @@ public class NewTestProjectCreationPage extends WizardPage { if (target != null) { mInternalMinSdkVersionUpdate = true; - mMinSdkVersionField.setText(Integer.toString(target.getApiVersionNumber())); + mMinSdkVersionField.setText(target.getVersion().getApiString()); mInternalMinSdkVersionUpdate = false; } @@ -1261,21 +1257,10 @@ public class NewTestProjectCreationPage extends WizardPage { return MSG_NONE; } - int version = AndroidManifestParser.INVALID_MIN_SDK; - try { - // If not empty, it must be a valid integer > 0 - version = Integer.parseInt(mInfo.getMinSdkVersion()); - } catch (NumberFormatException e) { - // ignore - } - - if (version < 1) { - return setStatus("Min SDK Version must be an integer > 0.", MSG_ERROR); - } - - if (mInfo.getSdkTarget() != null && mInfo.getSdkTarget().getApiVersionNumber() != version) { + if (mInfo.getSdkTarget() != null && + mInfo.getSdkTarget().getVersion().equals(mInfo.getMinSdkVersion()) == false) { return setStatus("The API level for the selected SDK target does not match the Min SDK version.", - MSG_WARNING); + mInfo.getSdkTarget().getVersion().isPreview() ? MSG_ERROR : MSG_WARNING); } return MSG_NONE; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileCreationPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileCreationPage.java index 8c242c0..4edfb77 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileCreationPage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileCreationPage.java @@ -87,10 +87,10 @@ class NewXmlFileCreationPage extends WizardPage { private final String mDefaultAttrs; private final String mDefaultRoot; private final int mTargetApiLevel; - + public TypeInfo(String uiName, - String tooltip, - ResourceFolderType resFolderType, + String tooltip, + ResourceFolderType resFolderType, Object rootSeed, String defaultRoot, String xmlns, @@ -110,12 +110,12 @@ class NewXmlFileCreationPage extends WizardPage { String getUiName() { return mUiName; } - - /** Returns the tooltip for the resource type. Can be null. */ + + /** Returns the tooltip for the resource type. Can be null. */ String getTooltip() { return mTooltip; } - + /** * Returns the name of the {@link ResourceFolderType}. * Never null but not necessarily unique, @@ -124,7 +124,7 @@ class NewXmlFileCreationPage extends WizardPage { String getResFolderName() { return mResFolderType.getName(); } - + /** * Returns the matching {@link ResourceFolderType}. * Never null but not necessarily unique, @@ -138,16 +138,16 @@ class NewXmlFileCreationPage extends WizardPage { void setWidget(Button widget) { mWidget = widget; } - + /** Returns the radio button associate with the resource type. Can be null. */ Button getWidget() { return mWidget; } - + /** * Returns the seed used to fill the root element values. * The seed might be either a String, a String array, an {@link ElementDescriptor}, - * a {@link DocumentDescriptor} or null. + * a {@link DocumentDescriptor} or null. */ Object getRootSeed() { return mRootSeed; @@ -278,7 +278,7 @@ class NewXmlFileCreationPage extends WizardPage { private static final String RES_FOLDER_ABS = AndroidConstants.WS_RESOURCES + AndroidConstants.WS_SEP; /** Relative destination folder root, e.g. "res/" */ private static final String RES_FOLDER_REL = SdkConstants.FD_RESOURCES + AndroidConstants.WS_SEP; - + private IProject mProject; private Text mProjectTextField; private Button mProjectBrowseButton; @@ -297,7 +297,7 @@ class NewXmlFileCreationPage extends WizardPage { private TypeInfo mCurrentTypeInfo; // --- UI creation --- - + /** * Constructs a new {@link NewXmlFileCreationPage}. * <p/> @@ -314,9 +314,9 @@ class NewXmlFileCreationPage extends WizardPage { /** * Called by the parent Wizard to create the UI for this Wizard Page. - * + * * {@inheritDoc} - * + * * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite) */ public void createControl(Composite parent) { @@ -347,7 +347,7 @@ class NewXmlFileCreationPage extends WizardPage { installTargetChangeListener(); validatePage(); } - + private void installTargetChangeListener() { mSdkTargetChangeListener = new ITargetChangeListener() { public void onProjectTargetChange(IProject changedProject) { @@ -364,18 +364,18 @@ class NewXmlFileCreationPage extends WizardPage { } } }; - + AdtPlugin.getDefault().addTargetListener(mSdkTargetChangeListener); } @Override public void dispose() { - + if (mSdkTargetChangeListener != null) { AdtPlugin.getDefault().removeTargetListener(mSdkTargetChangeListener); mSdkTargetChangeListener = null; } - + super.dispose(); } @@ -399,7 +399,7 @@ class NewXmlFileCreationPage extends WizardPage { public String getWsFolderPath() { return mWsFolderPathTextField == null ? "" : mWsFolderPathTextField.getText(); //$NON-NLS-1$ } - + /** * Returns an {@link IFile} on the destination file. @@ -426,7 +426,7 @@ class NewXmlFileCreationPage extends WizardPage { /** * Returns the {@link TypeInfo} for the currently selected type radio button. * Returns null if no radio button is selected. - * + * * @return A {@link TypeInfo} or null. */ public TypeInfo getSelectedType() { @@ -439,10 +439,10 @@ class NewXmlFileCreationPage extends WizardPage { } return type; } - + /** * Returns the selected root element string, if any. - * + * * @return The selected root element string or null. */ public String getRootElement() { @@ -457,7 +457,7 @@ class NewXmlFileCreationPage extends WizardPage { /** * Helper method to create a new GridData with an horizontal span. - * + * * @param horizSpan The number of cells for the horizontal span. * @return A new GridData with the horizontal span. */ @@ -469,7 +469,7 @@ class NewXmlFileCreationPage extends WizardPage { /** * Helper method to create a new GridData with an horizontal span and a style. - * + * * @param horizSpan The number of cells for the horizontal span. * @param style The style, e.g. {@link GridData#FILL_HORIZONTAL} * @return A new GridData with the horizontal span and the style. @@ -482,7 +482,7 @@ class NewXmlFileCreationPage extends WizardPage { /** * Helper method that creates an empty cell in the parent composite. - * + * * @param parent The parent composite. */ private void emptyCell(Composite parent) { @@ -491,7 +491,7 @@ class NewXmlFileCreationPage extends WizardPage { /** * Pads the parent with empty cells to match the number of columns of the parent grid. - * + * * @param parent A grid layout with NUM_COL columns * @param col The current number of columns used. * @return 0, the new number of columns used, for convenience. @@ -511,7 +511,7 @@ class NewXmlFileCreationPage extends WizardPage { */ private void createProjectGroup(Composite parent) { int col = 0; - + // project name String tooltip = "The Android Project where the new resource file will be created."; Label label = new Label(parent, SWT.NONE); @@ -542,7 +542,7 @@ class NewXmlFileCreationPage extends WizardPage { ++col; col = padWithEmptyCells(parent, col); - + // file name tooltip = "The name of the resource file to create."; label = new Label(parent, SWT.NONE); @@ -572,7 +572,7 @@ class NewXmlFileCreationPage extends WizardPage { // separator Label label = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL); label.setLayoutData(newGridData(NUM_COL, GridData.GRAB_HORIZONTAL)); - + // label before type radios label = new Label(parent, SWT.NONE); label.setText("What type of resource would you like to create?"); @@ -584,7 +584,7 @@ class NewXmlFileCreationPage extends WizardPage { padWithEmptyCells(parent, 2); grid.setLayout(new GridLayout(NUM_COL, true /*makeColumnsEqualWidth*/)); - + SelectionListener radioListener = new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { @@ -594,7 +594,7 @@ class NewXmlFileCreationPage extends WizardPage { } } }; - + int n = sTypes.length; int num_lines = (n + NUM_COL/2) / NUM_COL; for (int line = 0, k = 0; line < num_lines; line++) { @@ -627,7 +627,7 @@ class NewXmlFileCreationPage extends WizardPage { mConfigSelector.setLayoutData(gd); mConfigSelector.setOnChangeListener(new onConfigSelectorUpdated()); emptyCell(parent); - + // folder name String tooltip = "The folder where the file will be generated, relative to the project."; label = new Label(parent, SWT.NONE); @@ -669,7 +669,7 @@ class NewXmlFileCreationPage extends WizardPage { mRootElementCombo.select(0); mRootElementCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); mRootElementCombo.setToolTipText(tooltip); - + padWithEmptyCells(parent, 2); } @@ -683,7 +683,7 @@ class NewXmlFileCreationPage extends WizardPage { * <li>The current folder, valid if it's a folder under /res</li> * <li>An existing filename, in which case the user will be asked whether to override it.</li> * <ul> - * + * * @param selection The selection when the wizard was initiated. */ private void initializeFromSelection(IStructuredSelection selection) { @@ -702,7 +702,7 @@ class NewXmlFileCreationPage extends WizardPage { if (element instanceof IAdaptable) { IResource res = (IResource) ((IAdaptable) element).getAdapter(IResource.class); IProject project = res != null ? res.getProject() : null; - + // Is this an Android project? try { if (project == null || !project.hasNature(AndroidConstants.NATURE)) { @@ -712,18 +712,18 @@ class NewXmlFileCreationPage extends WizardPage { // checking the nature failed, ignore this resource continue; } - + int score = 1; // we have a valid project at least IPath wsFolderPath = null; String fileName = null; if (res.getType() == IResource.FOLDER) { - wsFolderPath = res.getProjectRelativePath(); + wsFolderPath = res.getProjectRelativePath(); } else if (res.getType() == IResource.FILE) { fileName = res.getName(); wsFolderPath = res.getParent().getProjectRelativePath(); } - + // Disregard this folder selection if it doesn't point to /res/something if (wsFolderPath != null && wsFolderPath.segmentCount() > 1 && @@ -735,7 +735,7 @@ class NewXmlFileCreationPage extends WizardPage { } score += fileName != null ? 4 : 0; - + if (score > targetScore) { targetScore = score; targetProject = project; @@ -744,7 +744,7 @@ class NewXmlFileCreationPage extends WizardPage { } } } - + // Now set the UI accordingly if (targetScore > 0) { mProject = targetProject; @@ -764,7 +764,7 @@ class NewXmlFileCreationPage extends WizardPage { if (roots.size() > 0) { roots.clear(); } - + // depending of the type of the seed, initialize the root in different ways Object rootSeed = type.getRootSeed(); @@ -783,7 +783,7 @@ class NewXmlFileCreationPage extends WizardPage { // Note: if project is null, the root list will be empty since it has been // cleared above. - + // get the AndroidTargetData from the project IAndroidTarget target = null; AndroidTargetData data = null; @@ -793,7 +793,7 @@ class NewXmlFileCreationPage extends WizardPage { // A project should have a target. The target can be missing if the project // is an old project for which a target hasn't been affected or if the // target no longer exists in this SDK. Simply log the error and dismiss. - + AdtPlugin.log(IStatus.INFO, "NewXmlFile wizard: no platform target for project %s", //$NON-NLS-1$ mProject.getName()); @@ -807,14 +807,14 @@ class NewXmlFileCreationPage extends WizardPage { // loaded we can end up in a weird case where we have a target but it // doesn't have any data yet. // Lets log a warning and silently ignore this root. - + AdtPlugin.log(IStatus.INFO, "NewXmlFile wizard: no data for target %s, project %s", //$NON-NLS-1$ target.getName(), mProject.getName()); continue; } } - + IDescriptorProvider provider = data.getDescriptorProvider((Integer)rootSeed); ElementDescriptor descriptor = provider.getDescriptor(); if (descriptor != null) { @@ -842,22 +842,22 @@ class NewXmlFileCreationPage extends WizardPage { roots.add(xmlName); } } - + visited.add(desc); - + for (ElementDescriptor child : desc.getChildren()) { if (!visited.contains(child)) { initRootElementDescriptor(roots, child, visited); } } } - + /** * Callback called when the user edits the project text field. */ private void onProjectFieldUpdated() { String project = mProjectTextField.getText(); - + // Is this a valid project? IJavaProject[] projects = mProjectChooserHelper.getAndroidProjects(null /*javaModel*/); IProject found = null; @@ -896,15 +896,15 @@ class NewXmlFileCreationPage extends WizardPage { // enable types based on new API level enableTypesBasedOnApi(); - + // update the Type with the new descriptors. initializeRootValues(); - + // update the combo updateRootCombo(getSelectedType()); - + validatePage(); - } + } /** * Callback called when the Folder text field is changed, either programmatically @@ -929,7 +929,7 @@ class NewXmlFileCreationPage extends WizardPage { // We get "res/foo" from selections relative to the project when we want a "/res/foo" path. if (wsFolderPath.startsWith(RES_FOLDER_REL)) { wsFolderPath = RES_FOLDER_ABS + wsFolderPath.substring(RES_FOLDER_REL.length()); - + mInternalWsFolderPathUpdate = true; mWsFolderPathTextField.setText(wsFolderPath); mInternalWsFolderPathUpdate = false; @@ -937,7 +937,7 @@ class NewXmlFileCreationPage extends WizardPage { if (wsFolderPath.startsWith(RES_FOLDER_ABS)) { wsFolderPath = wsFolderPath.substring(RES_FOLDER_ABS.length()); - + int pos = wsFolderPath.indexOf(AndroidConstants.WS_SEP_CHAR); if (pos >= 0) { wsFolderPath = wsFolderPath.substring(0, pos); @@ -985,7 +985,7 @@ class NewXmlFileCreationPage extends WizardPage { /** * Callback called when one of the type radio button is changed. - * + * * @param typeWidget The type radio button that changed. */ private void onRadioTypeUpdated(Button typeWidget) { @@ -1003,13 +1003,13 @@ class NewXmlFileCreationPage extends WizardPage { break; } } - + if (type == null) { return; } // update the combo - + updateRootCombo(type); // update the folder path @@ -1022,7 +1022,7 @@ class NewXmlFileCreationPage extends WizardPage { if (qual == null) { // The configuration is valid. Reformat the folder path using the canonical // value from the configuration. - + newPath = RES_FOLDER_ABS + mTempConfig.getFolderName(type.getResFolderType()); } else { // The configuration is invalid. We still update the path but this time @@ -1049,7 +1049,7 @@ class NewXmlFileCreationPage extends WizardPage { * Helper method that fills the values of the "root element" combo box based * on the currently selected type radio button. Also disables the combo is there's * only one choice. Always select the first root element for the given type. - * + * * @param type The currently selected {@link TypeInfo}. Cannot be null. */ private void updateRootCombo(TypeInfo type) { @@ -1059,14 +1059,14 @@ class NewXmlFileCreationPage extends WizardPage { if (type != null) { // get the list of roots. The list can be empty but not null. ArrayList<String> roots = type.getRoots(); - + // enable the combo if there's more than one choice mRootElementCombo.setEnabled(roots != null && roots.size() > 1); - + for (String root : roots) { mRootElementCombo.add(root); } - + int index = 0; // default is to select the first one String defaultRoot = type.getDefaultRoot(); if (defaultRoot != null) { @@ -1086,16 +1086,16 @@ class NewXmlFileCreationPage extends WizardPage { } TypeInfo type = getSelectedType(); - + if (type != null) { mConfigSelector.getConfiguration(mTempConfig); StringBuffer sb = new StringBuffer(RES_FOLDER_ABS); sb.append(mTempConfig.getFolderName(type.getResFolderType())); - + mInternalWsFolderPathUpdate = true; mWsFolderPathTextField.setText(sb.toString()); mInternalWsFolderPathUpdate = false; - + validatePage(); } } @@ -1103,7 +1103,7 @@ class NewXmlFileCreationPage extends WizardPage { /** * Helper method to select on of the type radio buttons. - * + * * @param type The TypeInfo matching the radio button to selected or null to deselect them all. */ private void selectType(TypeInfo type) { @@ -1130,9 +1130,9 @@ class NewXmlFileCreationPage extends WizardPage { IAndroidTarget target = mProject != null ? Sdk.getCurrent().getTarget(mProject) : null; int currentApiLevel = 1; if (target != null) { - currentApiLevel = target.getApiVersionNumber(); + currentApiLevel = target.getVersion().getApiLevel(); } - + for (TypeInfo type : sTypes) { type.getWidget().setEnabled(type.getTargetApiLevel() <= currentApiLevel); } @@ -1175,7 +1175,7 @@ class NewXmlFileCreationPage extends WizardPage { IAndroidTarget target = Sdk.getCurrent().getTarget(mProject); int currentApiLevel = 1; if (target != null) { - currentApiLevel = target.getApiVersionNumber(); + currentApiLevel = target.getVersion().getApiLevel(); } TypeInfo type = getSelectedType(); diff --git a/sdkmanager/app/src/com/android/sdkmanager/Main.java b/sdkmanager/app/src/com/android/sdkmanager/Main.java index c95fa04..aab8066 100644 --- a/sdkmanager/app/src/com/android/sdkmanager/Main.java +++ b/sdkmanager/app/src/com/android/sdkmanager/Main.java @@ -416,7 +416,7 @@ class Main { mSdkLog.printf(" Name: %s\n", target.getName()); if (target.isPlatform()) { mSdkLog.printf(" Type: Platform\n"); - mSdkLog.printf(" API level: %d\n", target.getApiVersionNumber()); + mSdkLog.printf(" API level: %s\n", target.getVersion().getApiString()); mSdkLog.printf(" Revision: %d\n", target.getRevision()); } else { mSdkLog.printf(" Type: Add-On\n"); @@ -426,7 +426,7 @@ class Main { mSdkLog.printf(" Description: %s\n", target.getDescription()); } mSdkLog.printf(" Based on Android %s (API level %d)\n", - target.getApiVersionName(), target.getApiVersionNumber()); + target.getVersionName(), target.getVersion().getApiString()); // display the optional libraries. IOptionalLibrary[] libraries = target.getOptionalLibraries(); @@ -501,13 +501,13 @@ class Main { // get the target of the AVD IAndroidTarget target = info.getTarget(); if (target.isPlatform()) { - mSdkLog.printf(" Target: %s (API level %d)\n", target.getName(), - target.getApiVersionNumber()); + mSdkLog.printf(" Target: %s (API level %s)\n", target.getName(), + target.getVersion().getApiString()); } else { mSdkLog.printf(" Target: %s (%s)\n", target.getName(), target .getVendor()); - mSdkLog.printf(" Based on Android %s (API level %d)\n", target - .getApiVersionName(), target.getApiVersionNumber()); + mSdkLog.printf(" Based on Android %s (API level %s)\n", + target.getVersionName(), target.getVersion().getApiString()); } // display some extra values. diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java index 3ac0d1a..a4b50ed 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java @@ -137,14 +137,13 @@ final class AddOnTarget implements IAndroidTarget { return mDescription; } - public String getApiVersionName() { + public AndroidVersion getVersion() { // this is always defined by the base platform - return mBasePlatform.getApiVersionName(); + return mBasePlatform.getVersion(); } - public int getApiVersionNumber() { - // this is always defined by the base platform - return mBasePlatform.getApiVersionNumber(); + public String getVersionName() { + return mBasePlatform.getVersionName(); } public int getRevision() { @@ -182,7 +181,7 @@ final class AddOnTarget implements IAndroidTarget { return sampleLoc.getAbsolutePath(); } } - // INTENT FALL-THROUGH + // INTENDED FALL-THROUGH default : return mBasePlatform.getPath(pathId); } @@ -222,21 +221,22 @@ final class AddOnTarget implements IAndroidTarget { // if the receiver has no optional library, then anything with api version number >= to // the receiver is compatible. if (mLibraries.length == 0) { - return target.getApiVersionNumber() >= getApiVersionNumber(); + return target.getVersion().getApiLevel() >= getVersion().getApiLevel(); } // Otherwise, target is only compatible if the vendor and name are equals with the api // number greater or equal (ie target is a newer version of this add-on). if (target.isPlatform() == false) { return (mVendor.equals(target.getVendor()) && mName.equals(target.getName()) && - target.getApiVersionNumber() >= getApiVersionNumber()); + target.getVersion().getApiLevel() >= getVersion().getApiLevel()); } return false; } public String hashString() { - return String.format(ADD_ON_FORMAT, mVendor, mName, mBasePlatform.getApiVersionNumber()); + return String.format(ADD_ON_FORMAT, mVendor, mName, + mBasePlatform.getVersion().getApiLevel()); } @Override @@ -250,7 +250,7 @@ final class AddOnTarget implements IAndroidTarget { AddOnTarget addon = (AddOnTarget)obj; return mVendor.equals(addon.mVendor) && mName.equals(addon.mName) && - mBasePlatform.getApiVersionNumber() == addon.mBasePlatform.getApiVersionNumber(); + mBasePlatform.getVersion().getApiLevel() == addon.mBasePlatform.getVersion().getApiLevel(); } return super.equals(obj); @@ -277,7 +277,7 @@ final class AddOnTarget implements IAndroidTarget { // api version if (value == 0) { - value = getApiVersionNumber() - target.getApiVersionNumber(); + value = getVersion().getApiLevel() - target.getVersion().getApiLevel(); } return value; diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/AndroidVersion.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/AndroidVersion.java new file mode 100644 index 0000000..5bd9aad --- /dev/null +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/AndroidVersion.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sdklib; + +import java.util.Properties; + +/** + * Represents the version of a target or device. + * <p/> + * A version is defined by an API level and an optional code name. + * <ul><li>Release versions of the Android platform are identified by their API level (integer), + * (technically the code name for release version is "REL" but this class will return + * <code>null<code> instead.)</li> + * <li>Preview versions of the platform are identified by a code name. Their API level + * is usually set to the value of the previous platform.</li></ul> + * <p/> + * While this class contains both values, its goal is to abstract them, so that code comparing 2+ + * versions doesn't have to deal with the logic of handle both values. + * <p/> + * There are some cases where ones may want to access the values directly. This can be done + * with {@link #getApiLevel()} and {@link #getCodename()}. + * <p/> + * For generic UI display of the API version, {@link #getApiString()} is to be used. + * + */ +public class AndroidVersion { + + private static final String PROP_API_LEVEL = "AndroidVersion.ApiLevel"; //$NON-NLS-1$ + private static final String PROP_CODENAME = "AndroidVersion.CodeName"; //$NON-NLS-1$ + + private final int mApiLevel; + private final String mCodename; + + public AndroidVersion(int apiLevel, String codename) { + mApiLevel = apiLevel; + mCodename = codename; + } + + public AndroidVersion(Properties properties) { + throw new UnsupportedOperationException("TODO"); + } + + public void saveProperties(Properties props) { + props.setProperty(PROP_API_LEVEL, Integer.toString(mApiLevel)); + props.setProperty(PROP_CODENAME, mCodename); + } + + /** + * Returns the api level as an integer. + * <p/>For target that are in preview mode, this can be superseded by + * {@link #getCodename()}. + * <p/>To display the API level in the UI, use {@link #getApiString()}, which will use the + * codename if applicable. + * @see #getCodename() + * @see #getApiString() + */ + public int getApiLevel() { + return mApiLevel; + } + + /** + * Returns the version code name if applicable, null otherwise. + * <p/>If the codename is non null, then the API level should be ignored, and this should be + * used as a unique identifier of the target instead. + */ + public String getCodename() { + return mCodename; + } + + /** + * Returns a string representing the API level and/or the code name. + */ + public String getApiString() { + if (mCodename != null) { + return mCodename; + } + + return Integer.toString(mApiLevel); + } + + /** + * Returns whether or not the version is a preview version. + */ + public boolean isPreview() { + return mCodename != null; + } + + /** + * Checks whether a device running a version similar to the receiver can run a project compiled + * for the given <var>version</var>. + * <p/> + * Be aware that this is not a perfect test, as other properties could break compatibility + * despite this method returning true. For a more comprehensive test, see + * {@link IAndroidTarget#isCompatibleBaseFor(IAndroidTarget)}. + * <p/> + * Nevertheless, when testing if an application can run on a device (where there is no + * access to the list of optional libraries), this method can give a good indication of whether + * there is a chance the application could run, or if there's a direct incompatibility. + */ + public boolean canRun(AndroidVersion appVersion) { + // if the application is compiled for a preview version, the device must be running exactly + // the same. + if (appVersion.mCodename != null) { + return appVersion.mCodename.equals(mCodename); + } + + // otherwise, we check the api level (note that a device running a preview version + // will have the api level of the previous platform). + return mApiLevel >= appVersion.mApiLevel; + } + + /** + * Returns <code>true</code> if the AndroidVersion is an API level equals to + * <var>apiLevel</var>. + */ + public boolean equals(int apiLevel) { + return mCodename == null && apiLevel == mApiLevel; + } + + /** + * Compares the receiver with either an {@link AndroidVersion} object or a {@link String} + * object. + * <p/>If <var>obj</var> is a {@link String}, then the method will first check if it's a string + * representation of a number, in which case it'll compare it to the api level. Otherwise, it'll + * compare it against the code name. + * <p/>For all other type of object give as parameter, this method will return + * <code>false</code>. + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof AndroidVersion) { + AndroidVersion version = (AndroidVersion)obj; + + if (mCodename == null) { + return version.mCodename == null && + mApiLevel == version.mApiLevel; + } else { + return mCodename.equals(version.mCodename) && + mApiLevel == version.mApiLevel; + } + + } else if (obj instanceof String) { + try { + int value = Integer.parseInt((String)obj); + return value == mApiLevel; + } catch (NumberFormatException e) { + // not a number? compare to the code name + return obj.equals(mCodename); + } + } + + return false; + } + + @Override + public int hashCode() { + if (mCodename != null) { + return mCodename.hashCode(); + } + + // there may be some collisions between the hashcode of the codename and the api level + // but it's acceptable. + return mApiLevel; + } +} diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java index f64b7ea..095a593 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java @@ -119,14 +119,14 @@ public interface IAndroidTarget extends Comparable<IAndroidTarget> { String getDescription(); /** - * Returns the api version as an integer. + * Returns the version of the target. This is guaranteed to be non-null. */ - int getApiVersionNumber(); + AndroidVersion getVersion(); /** * Returns the platform version as a readable string. */ - String getApiVersionName(); + public String getVersionName(); /** Returns the revision number for the target. */ int getRevision(); diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java index ae621f3..911d379 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java @@ -26,32 +26,41 @@ import java.util.Map; */ final class PlatformTarget implements IAndroidTarget { /** String used to get a hash to the platform target */ - private final static String PLATFORM_HASH = "android-%d"; + private final static String PLATFORM_HASH_API_LEVEL = "android-%d"; + private final static String PLATFORM_HASH_CODENAME = "android-%s"; private final static String PLATFORM_VENDOR = "Android Open Source Project"; + private final static String PLATFORM_NAME = "Android %s"; + private final static String PLATFORM_NAME_PREVIEW = "Android %s (Preview)"; private final String mLocation; private final String mName; - private final int mApiVersionNumber; - private final String mApiVersionName; + private final AndroidVersion mVersion; + private final String mVersionName; private final int mRevision; private final Map<String, String> mProperties; private final Map<Integer, String> mPaths = new HashMap<Integer, String>(); private String[] mSkins; + PlatformTarget(String location, Map<String, String> properties, - int apiNumber, String apiName, int revision) { - mName = String.format(PLATFORM_NAME, apiName); + int apiLevel, String codeName, String versionName, int revision) { if (location.endsWith(File.separator) == false) { location = location + File.separator; } mLocation = location; mProperties = Collections.unmodifiableMap(properties); - mApiVersionNumber = apiNumber; - mApiVersionName = apiName; + mVersion = new AndroidVersion(apiLevel, codeName); + mVersionName = versionName; mRevision = revision; + if (mVersion.isPreview()) { + mName = String.format(PLATFORM_NAME_PREVIEW, mVersionName); + } else { + mName = String.format(PLATFORM_NAME, mVersionName); + } + // pre-build the path to the platform components mPaths.put(ANDROID_JAR, mLocation + SdkConstants.FN_FRAMEWORK_LIBRARY); mPaths.put(SOURCES, mLocation + SdkConstants.FD_ANDROID_SOURCES); @@ -119,15 +128,15 @@ final class PlatformTarget implements IAndroidTarget { * @see com.android.sdklib.IAndroidTarget#getDescription() */ public String getDescription() { - return String.format("Standard Android platform %s", mApiVersionName); + return String.format("Standard Android platform %s", mVersionName); } - public int getApiVersionNumber(){ - return mApiVersionNumber; + public AndroidVersion getVersion() { + return mVersion; } - public String getApiVersionName() { - return mApiVersionName; + public String getVersionName() { + return mVersionName; } public int getRevision() { @@ -189,13 +198,23 @@ final class PlatformTarget implements IAndroidTarget { return true; } + // if the platform has a codename (ie it's a preview of an upcoming platform), then + // both platform must be exactly identical. + if (mVersion.getCodename() != null) { + return equals(target); + } + // target is compatible wit the receiver as long as its api version number is greater or // equal. - return target.getApiVersionNumber() >= mApiVersionNumber; + return target.getVersion().getApiLevel() >= mVersion.getApiLevel(); } public String hashString() { - return String.format(PLATFORM_HASH, mApiVersionNumber); + if (mVersion.getCodename() != null) { + return String.format(PLATFORM_HASH_CODENAME, mVersion.getCodename()); + } + + return String.format(PLATFORM_HASH_API_LEVEL, mVersion.getApiLevel()); } @Override @@ -206,10 +225,12 @@ final class PlatformTarget implements IAndroidTarget { @Override public boolean equals(Object obj) { if (obj instanceof PlatformTarget) { - return mApiVersionNumber == ((PlatformTarget)obj).mApiVersionNumber; + PlatformTarget platform = (PlatformTarget)obj; + + return mVersion.equals(platform.getVersion()); } - return super.equals(obj); + return false; } /* @@ -223,7 +244,13 @@ final class PlatformTarget implements IAndroidTarget { return -1; } - return mApiVersionNumber - target.getApiVersionNumber(); + int apiDiff = mVersion.getApiLevel() - target.getVersion().getApiLevel(); + + if (mVersion.getCodename() != null && apiDiff == 0) { + return mVersion.getCodename().compareTo(target.getVersion().getCodename()); + } + + return apiDiff; } // ---- platform only methods. diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java index 8c87d44..7df2512 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java @@ -42,6 +42,7 @@ import java.util.regex.Pattern; public final class SdkManager { public final static String PROP_VERSION_SDK = "ro.build.version.sdk"; + public final static String PROP_VERSION_CODENAME = "ro.build.version.codename"; public final static String PROP_VERSION_RELEASE = "ro.build.version.release"; public final static String PROP_VERSION_REVISION = "ro.build.version.incremental"; @@ -263,23 +264,28 @@ public final class SdkManager { if (map != null) { // look for some specific values in the map. + + // version string String apiName = map.get(PROP_VERSION_RELEASE); if (apiName == null) { if (log != null) { log.error(null, "Ignoring platform '%1$s': %2$s is missing from '%3$s'", - platform.getName(), PROP_VERSION_RELEASE, SdkConstants.FN_BUILD_PROP); + platform.getName(), PROP_VERSION_RELEASE, + SdkConstants.FN_BUILD_PROP); } return null; } + // api level int apiNumber; String stringValue = map.get(PROP_VERSION_SDK); if (stringValue == null) { if (log != null) { log.error(null, "Ignoring platform '%1$s': %2$s is missing from '%3$s'", - platform.getName(), PROP_VERSION_SDK, SdkConstants.FN_BUILD_PROP); + platform.getName(), PROP_VERSION_SDK, + SdkConstants.FN_BUILD_PROP); } return null; } else { @@ -291,19 +297,28 @@ public final class SdkManager { if (log != null) { log.error(null, "Ignoring platform '%1$s': %2$s is not a valid number in %3$s.", - platform.getName(), PROP_VERSION_SDK, SdkConstants.FN_BUILD_PROP); + platform.getName(), PROP_VERSION_SDK, + SdkConstants.FN_BUILD_PROP); } return null; } } + // codename (optional) + String apiCodename = map.get(PROP_VERSION_CODENAME); + if (apiCodename != null && apiCodename.equals("REL")) { + apiCodename = null; // REL means it's a release version and therefore the + // codename is irrelevant at this point. + } + int revision = 1; stringValue = map.get(PROP_VERSION_REVISION); if (stringValue == null) { if (log != null) { log.error(null, "Ignoring platform '%1$s': %2$s is missing from '%3$s'", - platform.getName(), PROP_VERSION_REVISION, SdkConstants.FN_BUILD_PROP); + platform.getName(), PROP_VERSION_REVISION, + SdkConstants.FN_BUILD_PROP); } return null; } else { @@ -346,6 +361,7 @@ public final class SdkManager { platform.getAbsolutePath(), map, apiNumber, + apiCodename, apiName, revision); @@ -435,8 +451,7 @@ public final class SdkManager { try { int apiValue = Integer.parseInt(api); for (IAndroidTarget target : targetList) { - if (target.isPlatform() && - target.getApiVersionNumber() == apiValue) { + if (target.isPlatform() && target.getVersion().equals(apiValue)) { baseTarget = (PlatformTarget)target; break; } diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java index 7908322..43228c0 100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java @@ -16,6 +16,7 @@ package com.android.sdklib.internal.repository;
+import com.android.sdklib.AndroidVersion;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkConstants;
import com.android.sdklib.SdkManager;
@@ -36,13 +37,12 @@ import java.util.Properties; */
public class AddonPackage extends Package {
- private static final String PROP_API_LEVEL = "Addon.ApiLevel"; //$NON-NLS-1$
private static final String PROP_NAME = "Addon.Name"; //$NON-NLS-1$
private static final String PROP_VENDOR = "Addon.Vendor"; //$NON-NLS-1$
private final String mVendor;
private final String mName;
- private final int mApiLevel;
+ private final AndroidVersion mVersion;
/** An add-on library. */
public static class Lib {
@@ -74,7 +74,10 @@ public class AddonPackage extends Package { super(source, packageNode, licenses);
mVendor = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_VENDOR);
mName = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_NAME);
- mApiLevel = XmlParserUtils.getXmlInt (packageNode, SdkRepository.NODE_API_LEVEL, 0);
+ mVersion = new AndroidVersion(
+ XmlParserUtils.getXmlInt (packageNode, SdkRepository.NODE_API_LEVEL, 0),
+ null); // add-ons on platform previews is not supported, so the codename is always
+ // null in this case.
mLibs = parseLibs(XmlParserUtils.getFirstChild(packageNode, SdkRepository.NODE_LIBS));
}
@@ -88,7 +91,7 @@ public class AddonPackage extends Package { AddonPackage(IAndroidTarget target, Properties props) {
super( null, //source
props, //properties
- 0, //revision
+ target.getRevision(), //revision
null, //license
target.getDescription(), //description
null, //descUrl
@@ -97,7 +100,7 @@ public class AddonPackage extends Package { target.getLocation() //archiveOsPath
);
- mApiLevel = target.getApiVersionNumber();
+ mVersion = target.getVersion();
mName = target.getName();
mVendor = target.getVendor();
@@ -114,13 +117,13 @@ public class AddonPackage extends Package { /**
* Save the properties of the current packages in the given {@link Properties} object.
- * These properties will later be give the constructor that takes a {@link Properties} object.
+ * These properties will later be given to a constructor that takes a {@link Properties} object.
*/
@Override
void saveProperties(Properties props) {
super.saveProperties(props);
- props.setProperty(PROP_API_LEVEL, Integer.toString(mApiLevel));
+ mVersion.saveProperties(props);
props.setProperty(PROP_NAME, mName);
props.setProperty(PROP_VENDOR, mVendor);
}
@@ -167,7 +170,8 @@ public class AddonPackage extends Package { /** Returns the api-level, an int > 0, for platform, add-on and doc packages. */
public int getApiLevel() {
- return mApiLevel;
+ // FIXME: return the AndroidVersion instead.
+ return mVersion.getApiLevel();
}
/** Returns the libs defined in this add-on. Can be an empty array but not null. */
@@ -214,7 +218,7 @@ public class AddonPackage extends Package { // First find if this add-on is already installed. If so, reuse the same directory.
for (IAndroidTarget target : sdkManager.getTargets()) {
if (!target.isPlatform() &&
- target.getApiVersionNumber() == getApiLevel() &&
+ target.getVersion().equals(mVersion) &&
target.getName().equals(getName()) &&
target.getVendor().equals(getVendor())) {
return new File(target.getLocation());
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java index 1c0a638..3f36596 100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java @@ -16,6 +16,7 @@ package com.android.sdklib.internal.repository;
+import com.android.sdklib.AndroidVersion;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkConstants;
import com.android.sdklib.SdkManager;
@@ -34,11 +35,10 @@ import java.util.Properties; */
public class PlatformPackage extends Package {
- private static final String PROP_API_LEVEL = "Platform.ApiLevel"; //$NON-NLS-1$
private static final String PROP_VERSION = "Platform.Version"; //$NON-NLS-1$
- private final String mVersion;
- private final int mApiLevel;
+ private final AndroidVersion mVersion;
+ private final String mVersionName;
/**
* Creates a new platform package from the attributes and elements of the given XML node.
@@ -47,8 +47,13 @@ public class PlatformPackage extends Package { */
PlatformPackage(RepoSource source, Node packageNode, Map<String,String> licenses) {
super(source, packageNode, licenses);
- mVersion = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_VERSION);
- mApiLevel = XmlParserUtils.getXmlInt (packageNode, SdkRepository.NODE_API_LEVEL, 0);
+ mVersionName = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_VERSION);
+ int apiLevel = XmlParserUtils.getXmlInt (packageNode, SdkRepository.NODE_API_LEVEL, 0);
+ String codeName = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_API_CODENAME);
+ if (codeName.length() == 0) {
+ codeName = null;
+ }
+ mVersion = new AndroidVersion(apiLevel, codeName);
}
/**
@@ -60,7 +65,7 @@ public class PlatformPackage extends Package { PlatformPackage(IAndroidTarget target, Properties props) {
super( null, //source
props, //properties
- 0, //revision
+ target.getRevision(), //revision
null, //license
target.getDescription(), //description
null, //descUrl
@@ -69,37 +74,43 @@ public class PlatformPackage extends Package { target.getLocation() //archiveOsPath
);
- mApiLevel = target.getApiVersionNumber();
- mVersion = target.getApiVersionName();
+ mVersion = target.getVersion();
+ mVersionName = target.getVersionName();
}
/**
* Save the properties of the current packages in the given {@link Properties} object.
- * These properties will later be give the constructor that takes a {@link Properties} object.
+ * These properties will later be given to a constructor that takes a {@link Properties} object.
*/
@Override
void saveProperties(Properties props) {
super.saveProperties(props);
- props.setProperty(PROP_API_LEVEL, Integer.toString(mApiLevel));
- props.setProperty(PROP_VERSION, mVersion);
+ mVersion.saveProperties(props);
+ props.setProperty(PROP_VERSION, mVersionName);
}
/** Returns the version, a string, for platform packages. */
- public String getVersion() {
- return mVersion;
+ public String getVersionName() {
+ return mVersionName;
}
/** Returns the api-level, an int > 0, for platform, add-on and doc packages. */
public int getApiLevel() {
- return mApiLevel;
+ // FIXME: return the AndroidVersion instead.
+ return mVersion.getApiLevel();
}
/** Returns a short description for an {@link IDescription}. */
@Override
public String getShortDescription() {
+ if (mVersion.isPreview()) {
+ return String.format("SDK Platform Android %1$s (Preview)",
+ getVersionName());
+ }
+
return String.format("SDK Platform Android %1$s, API %2$d",
- getVersion(),
+ getVersionName(),
getApiLevel());
}
@@ -128,17 +139,17 @@ public class PlatformPackage extends Package { @Override
public File getInstallFolder(String osSdkRoot, String suggestedDir, SdkManager sdkManager) {
- // First find if this add-on is already installed. If so, reuse the same directory.
+ // First find if this platform is already installed. If so, reuse the same directory.
for (IAndroidTarget target : sdkManager.getTargets()) {
if (target.isPlatform() &&
- target.getApiVersionNumber() == getApiLevel() &&
- target.getApiVersionName().equals(getVersion())) {
+ target.getVersion().equals(mVersion) &&
+ target.getVersionName().equals(getVersionName())) {
return new File(target.getLocation());
}
}
File platforms = new File(osSdkRoot, SdkConstants.FD_PLATFORMS);
- File folder = new File(platforms, String.format("android-%s", getVersion())); //$NON-NLS-1$
+ File folder = new File(platforms, String.format("android-%s", getVersionName())); //$NON-NLS-1$
return folder;
}
@@ -162,7 +173,7 @@ public class PlatformPackage extends Package { }
PlatformPackage newPkg = (PlatformPackage) replacementPackage;
- return newPkg.getVersion().equalsIgnoreCase(this.getVersion()) &&
+ return newPkg.getVersionName().equalsIgnoreCase(this.getVersionName()) &&
newPkg.getApiLevel() == this.getApiLevel() &&
newPkg.getRevision() > this.getRevision();
}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkRepository.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkRepository.java index 9760444..3e9ab99 100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkRepository.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkRepository.java @@ -63,6 +63,8 @@ public class SdkRepository { public static final String NODE_VERSION = "version"; //$NON-NLS-1$
/** The api-level, an int > 0, for platform, add-on and doc packages. */
public static final String NODE_API_LEVEL = "api-level"; //$NON-NLS-1$
+ /** The api-codename, a string, for platform packages. */
+ public static final String NODE_API_CODENAME = "api-codename"; //$NON-NLS-1$
/** The vendor, a string, for add-on packages. */
public static final String NODE_VENDOR = "vendor"; //$NON-NLS-1$
/** The name, a string, for add-on packages or for libraries. */
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/sdk-repository.xsd b/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/sdk-repository.xsd index c55e7d5..920823a 100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/sdk-repository.xsd +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/sdk-repository.xsd @@ -52,6 +52,8 @@ <xsd:element name="version" type="xsd:normalizedString" /> <!-- The Android API Level for the platform. An int > 0. --> <xsd:element name="api-level" type="xsd:positiveInteger" /> + <!-- The optional codename for this platform, if it's a preview. --> + <xsd:element name="api-codename" type="xsd:string" minOccurs="0" /> <!-- The revision, an int > 0, incremented each time a new package is generated. --> diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/repository/repository_sample.xml b/sdkmanager/libs/sdklib/tests/com/android/sdklib/repository/repository_sample.xml index d4eacf9..68e8efd 100755 --- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/repository/repository_sample.xml +++ b/sdkmanager/libs/sdklib/tests/com/android/sdklib/repository/repository_sample.xml @@ -156,6 +156,24 @@ </sdk:libs>
<sdk:uses-license ref="license2" />
</sdk:add-on>
+ + <sdk:platform> + <sdk:version>Pastry</sdk:version> + <sdk:api-level>5</sdk:api-level> + <sdk:api-codename>Pastry</sdk:api-codename> + <sdk:revision>3</sdk:revision> + <sdk:uses-license ref="license1" /> + <sdk:description>Preview version for Pastry</sdk:description> + <sdk:desc-url>http://www.example.com/platform1.html</sdk:desc-url> + <!-- The archives node is mandatory and it cannot be empty. --> + <sdk:archives> + <sdk:archive os="any"> + <sdk:size>65536</sdk:size> + <sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum> + <sdk:url>http://www.example.com/files/plat1.zip</sdk:url> + </sdk:archive> + </sdk:archives> + </sdk:platform> <sdk:tool>
<sdk:revision>1</sdk:revision>
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdCreationDialog.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdCreationDialog.java index 8804c70..3c0369f 100644 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdCreationDialog.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdCreationDialog.java @@ -273,14 +273,14 @@ final class AvdCreationDialog extends Dialog { for (IAndroidTarget target : sdkManager.getTargets()) { String name; if (target.isPlatform()) { - name = String.format("%s - API Level %d", + name = String.format("%s - API Level %s", target.getName(), - target.getApiVersionNumber()); + target.getVersion().getApiString()); } else { - name = String.format("%s (%s) - API Level %d", + name = String.format("%s (%s) - API Level %s", target.getName(), target.getVendor(), - target.getApiVersionNumber()); + target.getVersion().getApiString()); } mCurrentTargets.put(name, target); mTargetCombo.add(name); diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdDetailsDialog.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdDetailsDialog.java index 8b054f2..a845056 100644 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdDetailsDialog.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdDetailsDialog.java @@ -16,6 +16,7 @@ package com.android.sdkuilib.internal.widgets; +import com.android.sdklib.AndroidVersion; import com.android.sdklib.IAndroidTarget; import com.android.sdklib.internal.avd.AvdManager; import com.android.sdklib.internal.avd.AvdManager.AvdInfo; @@ -106,8 +107,9 @@ final class AvdDetailsDialog extends Dialog { displayValue(c, "Error:", mAvdInfo.getErrorMessage()); } else { IAndroidTarget target = mAvdInfo.getTarget(); - displayValue(c, "Target:", String.format("%s (API level %d)", - target.getName(), target.getApiVersionNumber())); + AndroidVersion version = target.getVersion(); + displayValue(c, "Target:", String.format("%s (API level %s)", + target.getName(), version.getApiString())); // display some extra values. Map<String, String> properties = mAvdInfo.getProperties(); diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdSelector.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdSelector.java index 8826b4c..b27636c 100644 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdSelector.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdSelector.java @@ -723,8 +723,8 @@ public final class AvdSelector { IAndroidTarget target = avd.getTarget(); if (target != null) { item.setText(1, target.getFullName()); - item.setText(2, target.getApiVersionName()); - item.setText(3, Integer.toString(target.getApiVersionNumber())); + item.setText(2, target.getVersionName()); + item.setText(3, target.getVersion().getApiString()); } else { item.setText(1, "?"); item.setText(2, "?"); diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/SdkTargetSelector.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/SdkTargetSelector.java index bf420f3..e82eb29 100644 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/SdkTargetSelector.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/SdkTargetSelector.java @@ -323,8 +323,8 @@ public class SdkTargetSelector { item.setData(target); item.setText(0, target.getName()); item.setText(1, target.getVendor()); - item.setText(2, target.getApiVersionName()); - item.setText(3, Integer.toString(target.getApiVersionNumber())); + item.setText(2, target.getVersionName()); + item.setText(3, target.getVersion().getApiString()); } } else { table.setEnabled(false); |