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