1 /* 2 * Copyright (C) 2007 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.sdkstats; 18 19 import com.android.prefs.AndroidLocation; 20 import com.android.prefs.AndroidLocation.AndroidLocationException; 21 22 import org.eclipse.jface.preference.PreferenceStore; 23 24 import java.io.File; 25 import java.io.FileOutputStream; 26 import java.io.IOException; 27 import java.util.Random; 28 29 /** 30 * Manages persistence settings for DDMS. 31 * 32 * For convenience, this also stores persistence settings related to the "server stats" ping 33 * as well as some ADT settings that are SDK specific but not workspace specific. 34 */ 35 public class DdmsPreferenceStore { 36 37 public final static String PING_OPT_IN = "pingOptIn"; //$NON-NLS-1$ 38 private final static String PING_TIME = "pingTime"; //$NON-NLS-1$ 39 private final static String PING_ID = "pingId"; //$NON-NLS-1$ 40 41 private final static String ADT_USED = "adtUsed"; //$NON-NLS-1$ 42 private final static String LAST_SDK_PATH = "lastSdkPath"; //$NON-NLS-1$ 43 44 /** 45 * PreferenceStore for DDMS. 46 * Creation and usage must be synchronized on {@code DdmsPreferenceStore.class}. 47 * Don't use it directly, instead retrieve it via {@link #getPreferenceStore()}. 48 */ 49 private static volatile PreferenceStore sPrefStore; 50 51 public DdmsPreferenceStore() { 52 } 53 54 /** 55 * Returns the DDMS {@link PreferenceStore}. 56 * This keeps a static reference on the store, so consequent calls will 57 * return always the same store. 58 */ 59 public PreferenceStore getPreferenceStore() { 60 synchronized (DdmsPreferenceStore.class) { 61 if (sPrefStore == null) { 62 // get the location of the preferences 63 String homeDir = null; 64 try { 65 homeDir = AndroidLocation.getFolder(); 66 } catch (AndroidLocationException e1) { 67 // pass, we'll do a dummy store since homeDir is null 68 } 69 70 if (homeDir == null) { 71 sPrefStore = new PreferenceStore(); 72 return sPrefStore; 73 } 74 75 assert homeDir != null; 76 77 String rcFileName = homeDir + "ddms.cfg"; //$NON-NLS-1$ 78 79 // also look for an old pref file in the previous location 80 String oldPrefPath = System.getProperty("user.home") //$NON-NLS-1$ 81 + File.separator + ".ddmsrc"; //$NON-NLS-1$ 82 File oldPrefFile = new File(oldPrefPath); 83 if (oldPrefFile.isFile()) { 84 try { 85 PreferenceStore oldStore = new PreferenceStore(oldPrefPath); 86 oldStore.load(); 87 88 oldStore.save(new FileOutputStream(rcFileName), ""); //$NON-NLS-1$ 89 oldPrefFile.delete(); 90 91 PreferenceStore newStore = new PreferenceStore(rcFileName); 92 newStore.load(); 93 sPrefStore = newStore; 94 } catch (IOException e) { 95 // create a new empty store. 96 sPrefStore = new PreferenceStore(rcFileName); 97 } 98 } else { 99 sPrefStore = new PreferenceStore(rcFileName); 100 101 try { 102 sPrefStore.load(); 103 } catch (IOException e) { 104 System.err.println("Error Loading DDMS Preferences"); 105 } 106 } 107 } 108 109 assert sPrefStore != null; 110 return sPrefStore; 111 } 112 } 113 114 /** 115 * Save the prefs to the config file. 116 */ 117 public void save() { 118 PreferenceStore prefs = getPreferenceStore(); 119 synchronized (DdmsPreferenceStore.class) { 120 try { 121 prefs.save(); 122 } 123 catch (IOException ioe) { 124 // FIXME com.android.dmmlib.Log.w("ddms", "Failed saving prefs file: " + ioe.getMessage()); 125 } 126 } 127 } 128 129 // ---- Utility methods to access some specific prefs ---- 130 131 /** 132 * Indicates whether the ping ID is set. 133 * This should be true when {@link #isPingOptIn()} is true. 134 * 135 * @return true if a ping ID is set, which means the user gave permission 136 * to use the ping service. 137 */ 138 public boolean hasPingId() { 139 PreferenceStore prefs = getPreferenceStore(); 140 synchronized (DdmsPreferenceStore.class) { 141 return prefs != null && prefs.contains(PING_ID); 142 } 143 } 144 145 /** 146 * Retrieves the current ping ID, if set. 147 * To know if the ping ID is set, use {@link #hasPingId()}. 148 * <p/> 149 * There is no magic value reserved for "missing ping id or invalid store". 150 * The only proper way to know if the ping id is missing is to use {@link #hasPingId()}. 151 */ 152 public long getPingId() { 153 PreferenceStore prefs = getPreferenceStore(); 154 synchronized (DdmsPreferenceStore.class) { 155 // Note: getLong() returns 0L if the ID is missing so we do that too when 156 // there's no store. 157 return prefs == null ? 0L : prefs.getLong(PING_ID); 158 } 159 } 160 161 /** 162 * Generates a new random ping ID and saves it in the preference store. 163 * 164 * @return The new ping ID. 165 */ 166 public long generateNewPingId() { 167 PreferenceStore prefs = getPreferenceStore(); 168 169 Random rnd = new Random(); 170 long id = rnd.nextLong(); 171 172 synchronized (DdmsPreferenceStore.class) { 173 prefs.setValue(PING_ID, id); 174 try { 175 prefs.save(); 176 } catch (IOException e) { 177 /* ignore exceptions while saving preferences */ 178 } 179 } 180 181 return id; 182 } 183 184 /** 185 * Returns the "ping opt in" value from the preference store. 186 * This would be true if there's a valid preference store and 187 * the user opted for sending ping statistics. 188 */ 189 public boolean isPingOptIn() { 190 PreferenceStore prefs = getPreferenceStore(); 191 synchronized (DdmsPreferenceStore.class) { 192 return prefs != null && prefs.contains(PING_OPT_IN); 193 } 194 } 195 196 /** 197 * Saves the "ping opt in" value in the preference store. 198 * 199 * @param optIn The new user opt-in value. 200 */ 201 public void setPingOptIn(boolean optIn) { 202 PreferenceStore prefs = getPreferenceStore(); 203 204 synchronized (DdmsPreferenceStore.class) { 205 prefs.setValue(PING_OPT_IN, optIn); 206 try { 207 prefs.save(); 208 } catch (IOException e) { 209 /* ignore exceptions while saving preferences */ 210 } 211 } 212 } 213 214 /** 215 * Retrieves the ping time for the given app from the preference store. 216 * Callers should use {@link System#currentTimeMillis()} for time stamps. 217 * 218 * @param app The app name identifier. 219 * @return 0L if we don't have a preference store or there was no time 220 * recorded in the store for the requested app. Otherwise the time stamp 221 * from the store. 222 */ 223 public long getPingTime(String app) { 224 PreferenceStore prefs = getPreferenceStore(); 225 String timePref = PING_TIME + "." + app; //$NON-NLS-1$ 226 synchronized (DdmsPreferenceStore.class) { 227 return prefs == null ? 0 : prefs.getLong(timePref); 228 } 229 } 230 231 /** 232 * Sets the ping time for the given app from the preference store. 233 * Callers should use {@link System#currentTimeMillis()} for time stamps. 234 * 235 * @param app The app name identifier. 236 * @param timeStamp The time stamp from the store. 237 * 0L is a special value that should not be used. 238 */ 239 public void setPingTime(String app, long timeStamp) { 240 PreferenceStore prefs = getPreferenceStore(); 241 String timePref = PING_TIME + "." + app; //$NON-NLS-1$ 242 synchronized (DdmsPreferenceStore.class) { 243 prefs.setValue(timePref, timeStamp); 244 try { 245 prefs.save(); 246 } catch (IOException ioe) { 247 /* ignore exceptions while saving preferences */ 248 } 249 } 250 } 251 252 /** 253 * True if this is the first time the users runs ADT, which is detected by 254 * the lack of the setting set using {@link #setAdtUsed(boolean)} 255 * or this value being set to true. 256 * 257 * @return true if ADT has been used before 258 * 259 * @see #setAdtUsed(boolean) 260 */ 261 public boolean isAdtUsed() { 262 PreferenceStore prefs = getPreferenceStore(); 263 synchronized (DdmsPreferenceStore.class) { 264 if (prefs == null || !prefs.contains(ADT_USED)) { 265 return false; 266 } 267 return prefs.getBoolean(ADT_USED); 268 } 269 } 270 271 /** 272 * Sets whether the ADT startup wizard has been shown. 273 * ADT sets first to false once the welcome wizard has been shown once. 274 * 275 * @param used true if ADT has been used 276 */ 277 public void setAdtUsed(boolean used) { 278 PreferenceStore prefs = getPreferenceStore(); 279 synchronized (DdmsPreferenceStore.class) { 280 prefs.setValue(ADT_USED, used); 281 try { 282 prefs.save(); 283 } catch (IOException ioe) { 284 /* ignore exceptions while saving preferences */ 285 } 286 } 287 } 288 289 /** 290 * Retrieves the last SDK OS path. 291 * <p/> 292 * This is just an information value, the path may not exist, may not 293 * even be on an existing file system and/or may not point to an SDK 294 * anymore. 295 * 296 * @return The last SDK OS path from the preference store, or null if 297 * there is no store or an empty string if it is not defined. 298 */ 299 public String getLastSdkPath() { 300 PreferenceStore prefs = getPreferenceStore(); 301 synchronized (DdmsPreferenceStore.class) { 302 return prefs == null ? null : prefs.getString(LAST_SDK_PATH); 303 } 304 } 305 306 /** 307 * Sets the last SDK OS path. 308 * 309 * @param osSdkPath The SDK OS Path. Can be null or empty. 310 */ 311 public void setLastSdkPath(String osSdkPath) { 312 PreferenceStore prefs = getPreferenceStore(); 313 synchronized (DdmsPreferenceStore.class) { 314 prefs.setValue(LAST_SDK_PATH, osSdkPath); 315 try { 316 prefs.save(); 317 } catch (IOException ioe) { 318 /* ignore exceptions while saving preferences */ 319 } 320 } 321 } 322 } 323