OSDN Git Service

Add support for preview versions of platforms.
authorXavier Ducrohet <xav@android.com>
Mon, 20 Jul 2009 21:43:50 +0000 (14:43 -0700)
committerXavier Ducrohet <xav@android.com>
Tue, 21 Jul 2009 01:11:21 +0000 (18:11 -0700)
ro.build.version.codename is a new property indicating whether a platform
is in its release form (value = REL) or in development (value = dev branch
name such as Donut). When the codename indicates a development/preview version
then the API level must be ignored and this codename is used as a unique
identifier of the platform.

IAndroidTarget has been changed to return an instance of a new class
AndroidVersion instead of the api level directly. This class helps deals with
the logic of comparing version from targets or devices.

This change impacts all of the sdk manager to deal with targets identified by
codename instead of api level. This in turn impacts everything that relies
on the sdkmanager: ADT (build, launch, project creation), the AVD manager,
the SDK updater.

28 files changed:
anttasks/src/com/android/ant/SetupTask.java
ddms/libs/ddmlib/src/com/android/ddmlib/IDevice.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/PreCompilerBuilder.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/PreCompilerDeltaVisitor.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/AndroidLaunchController.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/DelayedLaunchInfo.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/DeviceChooserDialog.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidManifestParser.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetParser.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectCreationPage.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewTestProjectCreationPage.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileCreationPage.java
sdkmanager/app/src/com/android/sdkmanager/Main.java
sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java
sdkmanager/libs/sdklib/src/com/android/sdklib/AndroidVersion.java [new file with mode: 0644]
sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java
sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java
sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java
sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java
sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java
sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkRepository.java
sdkmanager/libs/sdklib/src/com/android/sdklib/repository/sdk-repository.xsd
sdkmanager/libs/sdklib/tests/com/android/sdklib/repository/repository_sample.xml
sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdCreationDialog.java
sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdDetailsDialog.java
sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdSelector.java
sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/SdkTargetSelector.java

index 60f8c7e..7af9611 100644 (file)
@@ -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();
         }
index 7e90878..8096abd 100755 (executable)
@@ -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.
      */
index 711708f..c7fd289 100644 (file)
@@ -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) {
index 38ff480..d5e6365 100644 (file)
@@ -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
index aad1812..b834bcf 100644 (file)
@@ -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);
index aaa8e89..01c94fe 100644 (file)
@@ -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;
index d2fe5ae..8b61e27 100644 (file)
@@ -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
index 3deea23..d5fd4e7 100644 (file)
@@ -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;
index 5c468ec..a499137 100644 (file)
@@ -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;
     }
 }
index 0fe592a..d6cfeae 100644 (file)
 
 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;
index 95b6ea1..5067111 100644 (file)
@@ -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;
index 22c2749..94fd99d 100755 (executable)
@@ -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;
index 8c242c0..4edfb77 100644 (file)
@@ -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();
index c95fa04..aab8066 100644 (file)
@@ -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.
index 3ac0d1a..a4b50ed 100644 (file)
@@ -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 (file)
index 0000000..5bd9aad
--- /dev/null
@@ -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;
+    }
+}
index f64b7ea..095a593 100644 (file)
@@ -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();
index ae621f3..911d379 100644 (file)
@@ -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.
index 8c87d44..7df2512 100644 (file)
@@ -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;
                             }
index 7908322..43228c0 100755 (executable)
@@ -16,6 +16,7 @@
 \r
 package com.android.sdklib.internal.repository;\r
 \r
+import com.android.sdklib.AndroidVersion;\r
 import com.android.sdklib.IAndroidTarget;\r
 import com.android.sdklib.SdkConstants;\r
 import com.android.sdklib.SdkManager;\r
@@ -36,13 +37,12 @@ import java.util.Properties;
  */\r
 public class AddonPackage extends Package {\r
 \r
-    private static final String PROP_API_LEVEL = "Addon.ApiLevel";  //$NON-NLS-1$\r
     private static final String PROP_NAME      = "Addon.Name";      //$NON-NLS-1$\r
     private static final String PROP_VENDOR    = "Addon.Vendor";    //$NON-NLS-1$\r
 \r
     private final String mVendor;\r
     private final String mName;\r
-    private final int    mApiLevel;\r
+    private final AndroidVersion mVersion;\r
 \r
     /** An add-on library. */\r
     public static class Lib {\r
@@ -74,7 +74,10 @@ public class AddonPackage extends Package {
         super(source, packageNode, licenses);\r
         mVendor   = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_VENDOR);\r
         mName     = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_NAME);\r
-        mApiLevel = XmlParserUtils.getXmlInt   (packageNode, SdkRepository.NODE_API_LEVEL, 0);\r
+        mVersion = new AndroidVersion(\r
+                XmlParserUtils.getXmlInt   (packageNode, SdkRepository.NODE_API_LEVEL, 0),\r
+                null); // add-ons on platform previews is not supported, so the codename is always\r
+                       // null in this case.\r
 \r
         mLibs = parseLibs(XmlParserUtils.getFirstChild(packageNode, SdkRepository.NODE_LIBS));\r
     }\r
@@ -88,7 +91,7 @@ public class AddonPackage extends Package {
     AddonPackage(IAndroidTarget target, Properties props) {\r
         super(  null,                       //source\r
                 props,                      //properties\r
-                0,                          //revision\r
+                target.getRevision(),       //revision\r
                 null,                       //license\r
                 target.getDescription(),    //description\r
                 null,                       //descUrl\r
@@ -97,7 +100,7 @@ public class AddonPackage extends Package {
                 target.getLocation()        //archiveOsPath\r
                 );\r
 \r
-        mApiLevel = target.getApiVersionNumber();\r
+        mVersion = target.getVersion();\r
         mName     = target.getName();\r
         mVendor   = target.getVendor();\r
 \r
@@ -114,13 +117,13 @@ public class AddonPackage extends Package {
 \r
     /**\r
      * Save the properties of the current packages in the given {@link Properties} object.\r
-     * These properties will later be give the constructor that takes a {@link Properties} object.\r
+     * These properties will later be given to a constructor that takes a {@link Properties} object.\r
      */\r
     @Override\r
     void saveProperties(Properties props) {\r
         super.saveProperties(props);\r
 \r
-        props.setProperty(PROP_API_LEVEL, Integer.toString(mApiLevel));\r
+        mVersion.saveProperties(props);\r
         props.setProperty(PROP_NAME, mName);\r
         props.setProperty(PROP_VENDOR, mVendor);\r
     }\r
@@ -167,7 +170,8 @@ public class AddonPackage extends Package {
 \r
     /** Returns the api-level, an int > 0, for platform, add-on and doc packages. */\r
     public int getApiLevel() {\r
-        return mApiLevel;\r
+        // FIXME: return the AndroidVersion instead.\r
+        return mVersion.getApiLevel();\r
     }\r
 \r
     /** Returns the libs defined in this add-on. Can be an empty array but not null. */\r
@@ -214,7 +218,7 @@ public class AddonPackage extends Package {
         // First find if this add-on is already installed. If so, reuse the same directory.\r
         for (IAndroidTarget target : sdkManager.getTargets()) {\r
             if (!target.isPlatform() &&\r
-                    target.getApiVersionNumber() == getApiLevel() &&\r
+                    target.getVersion().equals(mVersion) &&\r
                     target.getName().equals(getName()) &&\r
                     target.getVendor().equals(getVendor())) {\r
                 return new File(target.getLocation());\r
index 1c0a638..3f36596 100755 (executable)
@@ -16,6 +16,7 @@
 \r
 package com.android.sdklib.internal.repository;\r
 \r
+import com.android.sdklib.AndroidVersion;\r
 import com.android.sdklib.IAndroidTarget;\r
 import com.android.sdklib.SdkConstants;\r
 import com.android.sdklib.SdkManager;\r
@@ -34,11 +35,10 @@ import java.util.Properties;
  */\r
 public class PlatformPackage extends Package {\r
 \r
-    private static final String PROP_API_LEVEL = "Platform.ApiLevel";  //$NON-NLS-1$\r
     private static final String PROP_VERSION   = "Platform.Version";   //$NON-NLS-1$\r
 \r
-    private final String mVersion;\r
-    private final int mApiLevel;\r
+    private final AndroidVersion mVersion;\r
+    private final String mVersionName;\r
 \r
     /**\r
      * Creates a new platform package from the attributes and elements of the given XML node.\r
@@ -47,8 +47,13 @@ public class PlatformPackage extends Package {
      */\r
     PlatformPackage(RepoSource source, Node packageNode, Map<String,String> licenses) {\r
         super(source, packageNode, licenses);\r
-        mVersion  = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_VERSION);\r
-        mApiLevel = XmlParserUtils.getXmlInt   (packageNode, SdkRepository.NODE_API_LEVEL, 0);\r
+        mVersionName = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_VERSION);\r
+        int apiLevel = XmlParserUtils.getXmlInt   (packageNode, SdkRepository.NODE_API_LEVEL, 0);\r
+        String codeName = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_API_CODENAME);\r
+        if (codeName.length() == 0) {\r
+            codeName = null;\r
+        }\r
+        mVersion = new AndroidVersion(apiLevel, codeName);\r
     }\r
 \r
     /**\r
@@ -60,7 +65,7 @@ public class PlatformPackage extends Package {
     PlatformPackage(IAndroidTarget target, Properties props) {\r
         super(  null,                       //source\r
                 props,                      //properties\r
-                0,                          //revision\r
+                target.getRevision(),       //revision\r
                 null,                       //license\r
                 target.getDescription(),    //description\r
                 null,                       //descUrl\r
@@ -69,37 +74,43 @@ public class PlatformPackage extends Package {
                 target.getLocation()        //archiveOsPath\r
                 );\r
 \r
-        mApiLevel = target.getApiVersionNumber();\r
-        mVersion  = target.getApiVersionName();\r
+        mVersion = target.getVersion();\r
+        mVersionName  = target.getVersionName();\r
     }\r
 \r
     /**\r
      * Save the properties of the current packages in the given {@link Properties} object.\r
-     * These properties will later be give the constructor that takes a {@link Properties} object.\r
+     * These properties will later be given to a constructor that takes a {@link Properties} object.\r
      */\r
     @Override\r
     void saveProperties(Properties props) {\r
         super.saveProperties(props);\r
 \r
-        props.setProperty(PROP_API_LEVEL, Integer.toString(mApiLevel));\r
-        props.setProperty(PROP_VERSION, mVersion);\r
+        mVersion.saveProperties(props);\r
+        props.setProperty(PROP_VERSION, mVersionName);\r
     }\r
 \r
     /** Returns the version, a string, for platform packages. */\r
-    public String getVersion() {\r
-        return mVersion;\r
+    public String getVersionName() {\r
+        return mVersionName;\r
     }\r
 \r
     /** Returns the api-level, an int > 0, for platform, add-on and doc packages. */\r
     public int getApiLevel() {\r
-        return mApiLevel;\r
+        // FIXME: return the AndroidVersion instead.\r
+        return mVersion.getApiLevel();\r
     }\r
 \r
     /** Returns a short description for an {@link IDescription}. */\r
     @Override\r
     public String getShortDescription() {\r
+        if (mVersion.isPreview()) {\r
+            return String.format("SDK Platform Android %1$s (Preview)",\r
+                    getVersionName());\r
+        }\r
+\r
         return String.format("SDK Platform Android %1$s, API %2$d",\r
-                getVersion(),\r
+                getVersionName(),\r
                 getApiLevel());\r
     }\r
 \r
@@ -128,17 +139,17 @@ public class PlatformPackage extends Package {
     @Override\r
     public File getInstallFolder(String osSdkRoot, String suggestedDir, SdkManager sdkManager) {\r
 \r
-        // First find if this add-on is already installed. If so, reuse the same directory.\r
+        // First find if this platform is already installed. If so, reuse the same directory.\r
         for (IAndroidTarget target : sdkManager.getTargets()) {\r
             if (target.isPlatform() &&\r
-                    target.getApiVersionNumber() == getApiLevel() &&\r
-                    target.getApiVersionName().equals(getVersion())) {\r
+                    target.getVersion().equals(mVersion) &&\r
+                    target.getVersionName().equals(getVersionName())) {\r
                 return new File(target.getLocation());\r
             }\r
         }\r
 \r
         File platforms = new File(osSdkRoot, SdkConstants.FD_PLATFORMS);\r
-        File folder = new File(platforms, String.format("android-%s", getVersion())); //$NON-NLS-1$\r
+        File folder = new File(platforms, String.format("android-%s", getVersionName())); //$NON-NLS-1$\r
 \r
         return folder;\r
     }\r
@@ -162,7 +173,7 @@ public class PlatformPackage extends Package {
         }\r
 \r
         PlatformPackage newPkg = (PlatformPackage) replacementPackage;\r
-        return newPkg.getVersion().equalsIgnoreCase(this.getVersion()) &&\r
+        return newPkg.getVersionName().equalsIgnoreCase(this.getVersionName()) &&\r
             newPkg.getApiLevel() == this.getApiLevel() &&\r
             newPkg.getRevision() > this.getRevision();\r
     }\r
index 9760444..3e9ab99 100755 (executable)
@@ -63,6 +63,8 @@ public class SdkRepository {
     public static final String NODE_VERSION   = "version";                      //$NON-NLS-1$\r
     /** The api-level, an int > 0, for platform, add-on and doc packages. */\r
     public static final String NODE_API_LEVEL = "api-level";                    //$NON-NLS-1$\r
+    /** The api-codename, a string, for platform packages. */\r
+    public static final String NODE_API_CODENAME = "api-codename";              //$NON-NLS-1$\r
     /** The vendor, a string, for add-on packages. */\r
     public static final String NODE_VENDOR    = "vendor";                       //$NON-NLS-1$\r
     /** The name, a string, for add-on packages or for libraries. */\r
index c55e7d5..920823a 100755 (executable)
@@ -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. -->
index d4eacf9..68e8efd 100755 (executable)
         </sdk:libs>\r
         <sdk:uses-license ref="license2" />\r
     </sdk:add-on>\r
+
+   <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>
 \r
     <sdk:tool>\r
         <sdk:revision>1</sdk:revision>\r
index 8804c70..3c0369f 100644 (file)
@@ -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);
index 8b054f2..a845056 100644 (file)
@@ -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();
index 8826b4c..b27636c 100644 (file)
@@ -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, "?");
index bf420f3..e82eb29 100644 (file)
@@ -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);