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 class 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 * Creates an {@link AndroidVersion} with the given api level and codename. 50 * Codename should be null for a release version, otherwise it's a preview codename. 51 */ AndroidVersion(int apiLevel, String codename)52 public AndroidVersion(int apiLevel, String codename) { 53 mApiLevel = apiLevel; 54 mCodename = codename; 55 } 56 57 /** 58 * Creates an {@link AndroidVersion} from {@link Properties}, with default values if the 59 * {@link Properties} object doesn't contain the expected values. 60 * <p/>The {@link Properties} is expected to have been filled with 61 * {@link #saveProperties(Properties)}. 62 */ AndroidVersion(Properties properties, int defaultApiLevel, String defaultCodeName)63 public AndroidVersion(Properties properties, int defaultApiLevel, String defaultCodeName) { 64 if (properties == null) { 65 mApiLevel = defaultApiLevel; 66 mCodename = defaultCodeName; 67 } else { 68 mApiLevel = Integer.parseInt(properties.getProperty(PROP_API_LEVEL, 69 Integer.toString(defaultApiLevel))); 70 mCodename = properties.getProperty(PROP_CODENAME, defaultCodeName); 71 } 72 } 73 saveProperties(Properties props)74 public void saveProperties(Properties props) { 75 props.setProperty(PROP_API_LEVEL, Integer.toString(mApiLevel)); 76 if (mCodename != null) { 77 props.setProperty(PROP_CODENAME, mCodename); 78 } 79 } 80 81 /** 82 * Returns the api level as an integer. 83 * <p/>For target that are in preview mode, this can be superseded by 84 * {@link #getCodename()}. 85 * <p/>To display the API level in the UI, use {@link #getApiString()}, which will use the 86 * codename if applicable. 87 * @see #getCodename() 88 * @see #getApiString() 89 */ getApiLevel()90 public int getApiLevel() { 91 return mApiLevel; 92 } 93 94 /** 95 * Returns the version code name if applicable, null otherwise. 96 * <p/>If the codename is non null, then the API level should be ignored, and this should be 97 * used as a unique identifier of the target instead. 98 */ getCodename()99 public String getCodename() { 100 return mCodename; 101 } 102 103 /** 104 * Returns a string representing the API level and/or the code name. 105 */ getApiString()106 public String getApiString() { 107 if (mCodename != null) { 108 return mCodename; 109 } 110 111 return Integer.toString(mApiLevel); 112 } 113 114 /** 115 * Returns whether or not the version is a preview version. 116 */ isPreview()117 public boolean isPreview() { 118 return mCodename != null; 119 } 120 121 /** 122 * Checks whether a device running a version similar to the receiver can run a project compiled 123 * for the given <var>version</var>. 124 * <p/> 125 * Be aware that this is not a perfect test, as other properties could break compatibility 126 * despite this method returning true. For a more comprehensive test, see 127 * {@link IAndroidTarget#isCompatibleBaseFor(IAndroidTarget)}. 128 * <p/> 129 * Nevertheless, when testing if an application can run on a device (where there is no 130 * access to the list of optional libraries), this method can give a good indication of whether 131 * there is a chance the application could run, or if there's a direct incompatibility. 132 */ canRun(AndroidVersion appVersion)133 public boolean canRun(AndroidVersion appVersion) { 134 // if the application is compiled for a preview version, the device must be running exactly 135 // the same. 136 if (appVersion.mCodename != null) { 137 return appVersion.mCodename.equals(mCodename); 138 } 139 140 // otherwise, we check the api level (note that a device running a preview version 141 // will have the api level of the previous platform). 142 return mApiLevel >= appVersion.mApiLevel; 143 } 144 145 /** 146 * Returns <code>true</code> if the AndroidVersion is an API level equals to 147 * <var>apiLevel</var>. 148 */ equals(int apiLevel)149 public boolean equals(int apiLevel) { 150 return mCodename == null && apiLevel == mApiLevel; 151 } 152 153 /** 154 * Compares the receiver with either an {@link AndroidVersion} object or a {@link String} 155 * object. 156 * <p/>If <var>obj</var> is a {@link String}, then the method will first check if it's a string 157 * representation of a number, in which case it'll compare it to the api level. Otherwise, it'll 158 * compare it against the code name. 159 * <p/>For all other type of object give as parameter, this method will return 160 * <code>false</code>. 161 */ 162 @Override equals(Object obj)163 public boolean equals(Object obj) { 164 if (obj instanceof AndroidVersion) { 165 AndroidVersion version = (AndroidVersion)obj; 166 167 if (mCodename == null) { 168 return version.mCodename == null && 169 mApiLevel == version.mApiLevel; 170 } else { 171 return mCodename.equals(version.mCodename) && 172 mApiLevel == version.mApiLevel; 173 } 174 175 } else if (obj instanceof String) { 176 // if we have a code name, this must match. 177 if (mCodename != null) { 178 return mCodename.equals(obj); 179 } 180 181 // else we try to convert to a int and compare to the api level 182 try { 183 int value = Integer.parseInt((String)obj); 184 return value == mApiLevel; 185 } catch (NumberFormatException e) { 186 // not a number? we'll return false below. 187 } 188 } 189 190 return false; 191 } 192 193 @Override hashCode()194 public int hashCode() { 195 if (mCodename != null) { 196 return mCodename.hashCode(); 197 } 198 199 // there may be some collisions between the hashcode of the codename and the api level 200 // but it's acceptable. 201 return mApiLevel; 202 } 203 } 204