• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.sdklib;
18 
19 import com.android.sdklib.repository.PkgProps;
20 
21 import java.util.Properties;
22 
23 /**
24  * Represents the version of a target or device.
25  * <p/>
26  * A version is defined by an API level and an optional code name.
27  * <ul><li>Release versions of the Android platform are identified by their API level (integer),
28  * (technically the code name for release version is "REL" but this class will return
29  * <code>null<code> instead.)</li>
30  * <li>Preview versions of the platform are identified by a code name. Their API level
31  * is usually set to the value of the previous platform.</li></ul>
32  * <p/>
33  * While this class contains both values, its goal is to abstract them, so that code comparing 2+
34  * versions doesn't have to deal with the logic of handle both values.
35  * <p/>
36  * There are some cases where ones may want to access the values directly. This can be done
37  * with {@link #getApiLevel()} and {@link #getCodename()}.
38  * <p/>
39  * For generic UI display of the API version, {@link #getApiString()} is to be used.
40  */
41 public final class AndroidVersion implements Comparable<AndroidVersion> {
42 
43     private final int mApiLevel;
44     private final String mCodename;
45 
46     /**
47      * Thrown when an {@link AndroidVersion} object could not be created.
48      * @see AndroidVersion#AndroidVersion(Properties)
49      */
50     public final static class AndroidVersionException extends Exception {
51         private static final long serialVersionUID = 1L;
52 
AndroidVersionException(String message, Throwable cause)53         AndroidVersionException(String message, Throwable cause) {
54             super(message, cause);
55         }
56     }
57 
58     /**
59      * Creates an {@link AndroidVersion} with the given api level and codename.
60      * Codename should be null for a release version, otherwise it's a preview codename.
61      */
AndroidVersion(int apiLevel, String codename)62     public AndroidVersion(int apiLevel, String codename) {
63         mApiLevel = apiLevel;
64         mCodename = codename;
65     }
66 
67     /**
68      * Creates an {@link AndroidVersion} from {@link Properties}, with default values if the
69      * {@link Properties} object doesn't contain the expected values.
70      * <p/>The {@link Properties} is expected to have been filled with
71      * {@link #saveProperties(Properties)}.
72      */
AndroidVersion(Properties properties, int defaultApiLevel, String defaultCodeName)73     public AndroidVersion(Properties properties, int defaultApiLevel, String defaultCodeName) {
74         if (properties == null) {
75             mApiLevel = defaultApiLevel;
76             mCodename = defaultCodeName;
77         } else {
78             mApiLevel = Integer.parseInt(properties.getProperty(PkgProps.VERSION_API_LEVEL,
79                     Integer.toString(defaultApiLevel)));
80             mCodename = properties.getProperty(PkgProps.VERSION_CODENAME, defaultCodeName);
81         }
82     }
83 
84     /**
85      * Creates an {@link AndroidVersion} from {@link Properties}. The properties must contain
86      * android version information, or an exception will be thrown.
87      * @throws AndroidVersionException if no Android version information have been found
88      *
89      * @see #saveProperties(Properties)
90      */
AndroidVersion(Properties properties)91     public AndroidVersion(Properties properties) throws AndroidVersionException {
92         Exception error = null;
93 
94         String apiLevel = properties.getProperty(PkgProps.VERSION_API_LEVEL, null/*defaultValue*/);
95         if (apiLevel != null) {
96             try {
97                 mApiLevel = Integer.parseInt(apiLevel);
98                 mCodename = properties.getProperty(PkgProps.VERSION_CODENAME, null/*defaultValue*/);
99                 return;
100             } catch (NumberFormatException e) {
101                 error = e;
102             }
103         }
104 
105         // reaching here means the Properties object did not contain the apiLevel which is required.
106         throw new AndroidVersionException(PkgProps.VERSION_API_LEVEL + " not found!", error);
107     }
108 
saveProperties(Properties props)109     public void saveProperties(Properties props) {
110         props.setProperty(PkgProps.VERSION_API_LEVEL, Integer.toString(mApiLevel));
111         if (mCodename != null) {
112             props.setProperty(PkgProps.VERSION_CODENAME, mCodename);
113         }
114     }
115 
116     /**
117      * Returns the api level as an integer.
118      * <p/>For target that are in preview mode, this can be superseded by
119      * {@link #getCodename()}.
120      * <p/>To display the API level in the UI, use {@link #getApiString()}, which will use the
121      * codename if applicable.
122      * @see #getCodename()
123      * @see #getApiString()
124      */
getApiLevel()125     public int getApiLevel() {
126         return mApiLevel;
127     }
128 
129     /**
130      * Returns the version code name if applicable, null otherwise.
131      * <p/>If the codename is non null, then the API level should be ignored, and this should be
132      * used as a unique identifier of the target instead.
133      */
getCodename()134     public String getCodename() {
135         return mCodename;
136     }
137 
138     /**
139      * Returns a string representing the API level and/or the code name.
140      */
getApiString()141     public String getApiString() {
142         if (mCodename != null) {
143             return mCodename;
144         }
145 
146         return Integer.toString(mApiLevel);
147     }
148 
149     /**
150      * Returns whether or not the version is a preview version.
151      */
isPreview()152     public boolean isPreview() {
153         return mCodename != null;
154     }
155 
156     /**
157      * Checks whether a device running a version similar to the receiver can run a project compiled
158      * for the given <var>version</var>.
159      * <p/>
160      * Be aware that this is not a perfect test, as other properties could break compatibility
161      * despite this method returning true. For a more comprehensive test, see
162      * {@link IAndroidTarget#canRunOn(IAndroidTarget)}.
163      * <p/>
164      * Nevertheless, when testing if an application can run on a device (where there is no
165      * access to the list of optional libraries), this method can give a good indication of whether
166      * there is a chance the application could run, or if there's a direct incompatibility.
167      */
canRun(AndroidVersion appVersion)168     public boolean canRun(AndroidVersion appVersion) {
169         // if the application is compiled for a preview version, the device must be running exactly
170         // the same.
171         if (appVersion.mCodename != null) {
172             return appVersion.mCodename.equals(mCodename);
173         }
174 
175         // otherwise, we check the api level (note that a device running a preview version
176         // will have the api level of the previous platform).
177         return mApiLevel >= appVersion.mApiLevel;
178     }
179 
180     /**
181      * Returns <code>true</code> if the AndroidVersion is an API level equals to
182      * <var>apiLevel</var>.
183      */
equals(int apiLevel)184     public boolean equals(int apiLevel) {
185         return mCodename == null && apiLevel == mApiLevel;
186     }
187 
188     /**
189      * Compares the receiver with either an {@link AndroidVersion} object or a {@link String}
190      * object.
191      * <p/>If <var>obj</var> is a {@link String}, then the method will first check if it's a string
192      * representation of a number, in which case it'll compare it to the api level. Otherwise, it'll
193      * compare it against the code name.
194      * <p/>For all other type of object give as parameter, this method will return
195      * <code>false</code>.
196      */
197     @Override
equals(Object obj)198     public boolean equals(Object obj) {
199         if (obj instanceof AndroidVersion) {
200             AndroidVersion version = (AndroidVersion)obj;
201 
202             if (mCodename == null) {
203                 return version.mCodename == null &&
204                         mApiLevel == version.mApiLevel;
205             } else {
206                 return mCodename.equals(version.mCodename) &&
207                         mApiLevel == version.mApiLevel;
208             }
209 
210         } else if (obj instanceof String) {
211             // if we have a code name, this must match.
212             if (mCodename != null) {
213                 return mCodename.equals(obj);
214             }
215 
216             // else we try to convert to a int and compare to the api level
217             try {
218                 int value = Integer.parseInt((String)obj);
219                 return value == mApiLevel;
220             } catch (NumberFormatException e) {
221                 // not a number? we'll return false below.
222             }
223         }
224 
225         return false;
226     }
227 
228     @Override
hashCode()229     public int hashCode() {
230         if (mCodename != null) {
231             return mCodename.hashCode();
232         }
233 
234         // there may be some collisions between the hashcode of the codename and the api level
235         // but it's acceptable.
236         return mApiLevel;
237     }
238 
239     /**
240      * Returns a string with the API Level and optional codename.
241      * Useful for debugging.
242      * For display purpose, please use {@link #getApiString()} instead.
243      */
244     @Override
toString()245     public String toString() {
246         String s = String.format("API %1$d", mApiLevel);        //$NON-NLS-1$
247         if (isPreview()) {
248             s += String.format(", %1$s preview", mCodename);    //$NON-NLS-1$
249         }
250         return s;
251     }
252 
253     /**
254      * Compares this object with the specified object for order. Returns a
255      * negative integer, zero, or a positive integer as this object is less
256      * than, equal to, or greater than the specified object.
257      *
258      * @param o the Object to be compared.
259      * @return a negative integer, zero, or a positive integer as this object is
260      *         less than, equal to, or greater than the specified object.
261      */
262     @Override
compareTo(AndroidVersion o)263     public int compareTo(AndroidVersion o) {
264         return compareTo(o.mApiLevel, o.mCodename);
265     }
266 
compareTo(int apiLevel, String codename)267     private int compareTo(int apiLevel, String codename) {
268         if (mCodename == null) {
269             if (codename == null) {
270                 return mApiLevel - apiLevel;
271             } else {
272                 if (mApiLevel == apiLevel) {
273                     return -1; // same api level but argument is a preview for next version
274                 }
275 
276                 return mApiLevel - apiLevel;
277             }
278         } else {
279             // 'this' is a preview
280             if (mApiLevel == apiLevel) {
281                 if (codename == null) {
282                     return +1;
283                 } else {
284                     return mCodename.compareTo(codename);    // strange case where the 2 previews
285                                                              // have different codename?
286                 }
287             } else {
288                 return mApiLevel - apiLevel;
289             }
290         }
291     }
292 
293     /**
294      * Compares this version with the specified API and returns true if this version
295      * is greater or equal than the requested API -- that is the current version is a
296      * suitable min-api-level for the argument API.
297      */
isGreaterOrEqualThan(int api)298     public boolean isGreaterOrEqualThan(int api) {
299         return compareTo(api, null /*codename*/) >= 0;
300     }
301 }
302