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.sdkuilib.internal.repository; 18 19 import com.android.annotations.NonNull; 20 import com.android.prefs.AndroidLocation; 21 import com.android.prefs.AndroidLocation.AndroidLocationException; 22 import com.android.utils.ILogger; 23 24 import java.io.File; 25 import java.io.FileInputStream; 26 import java.io.FileNotFoundException; 27 import java.io.FileOutputStream; 28 import java.io.IOException; 29 import java.util.ArrayList; 30 import java.util.List; 31 import java.util.Map.Entry; 32 import java.util.Properties; 33 34 /** 35 * Controller class to get settings values. Settings are kept in-memory. 36 * Users of this class must first load the settings before changing them and save 37 * them when modified. 38 * <p/> 39 * Settings are enumerated by constants in {@link ISettingsPage}. 40 */ 41 public class SettingsController { 42 43 private static final String SETTINGS_FILENAME = "androidtool.cfg"; //$NON-NLS-1$ 44 45 private final ILogger mSdkLog; 46 private final Settings mSettings; 47 48 public interface OnChangedListener { 49 public void onSettingsChanged(SettingsController controller, Settings oldSettings); 50 } 51 private final List<OnChangedListener> mChangedListeners = new ArrayList<OnChangedListener>(1); 52 53 /** The currently associated {@link ISettingsPage}. Can be null. */ 54 private ISettingsPage mSettingsPage; 55 56 /** 57 * Constructs a new default {@link SettingsController}. 58 * 59 * @param sdkLog A non-null logger to use. 60 */ 61 public SettingsController(@NonNull ILogger sdkLog) { 62 mSdkLog = sdkLog; 63 mSettings = new Settings(); 64 } 65 66 /** 67 * Specialized constructor that wraps an existing {@link Settings} instance. 68 * This is mostly used in unit-tests to override settings that are being used. 69 * Normal usage should NOT need to call this constructor. 70 * 71 * @param sdkLog A non-null logger to use. 72 * @param settings A non-null {@link Settings} to use as-is. It is not duplicated. 73 */ 74 protected SettingsController(@NonNull ILogger sdkLog, @NonNull Settings settings) { 75 mSdkLog = sdkLog; 76 mSettings = settings; 77 } 78 79 public Settings getSettings() { 80 return mSettings; 81 } 82 83 public void registerOnChangedListener(OnChangedListener listener) { 84 if (listener != null && !mChangedListeners.contains(listener)) { 85 mChangedListeners.add(listener); 86 } 87 } 88 89 public void unregisterOnChangedListener(OnChangedListener listener) { 90 if (listener != null) { 91 mChangedListeners.remove(listener); 92 } 93 } 94 95 //--- Access to settings ------------ 96 97 98 public static class Settings { 99 private final Properties mProperties; 100 101 /** Initialize an empty set of settings. */ 102 public Settings() { 103 mProperties = new Properties(); 104 } 105 106 /** Duplicates a set of settings. */ 107 public Settings(Settings settings) { 108 this(); 109 for (Entry<Object, Object> entry : settings.mProperties.entrySet()) { 110 mProperties.put(entry.getKey(), entry.getValue()); 111 } 112 } 113 114 /** 115 * Specialized constructor for unit-tests that wraps an existing 116 * {@link Properties} instance. The properties instance is not duplicated, 117 * it's merely used as-is and changes will be reflected directly. 118 */ 119 protected Settings(Properties properties) { 120 mProperties = properties; 121 } 122 123 /** 124 * Returns the value of the {@link ISettingsPage#KEY_FORCE_HTTP} setting. 125 * 126 * @see ISettingsPage#KEY_FORCE_HTTP 127 */ 128 public boolean getForceHttp() { 129 return Boolean.parseBoolean(mProperties.getProperty(ISettingsPage.KEY_FORCE_HTTP)); 130 } 131 132 /** 133 * Returns the value of the {@link ISettingsPage#KEY_ASK_ADB_RESTART} setting. 134 * 135 * @see ISettingsPage#KEY_ASK_ADB_RESTART 136 */ 137 public boolean getAskBeforeAdbRestart() { 138 return Boolean.parseBoolean(mProperties.getProperty(ISettingsPage.KEY_ASK_ADB_RESTART)); 139 } 140 141 /** 142 * Returns the value of the {@link ISettingsPage#KEY_USE_DOWNLOAD_CACHE} setting. 143 * 144 * @see ISettingsPage#KEY_USE_DOWNLOAD_CACHE 145 */ 146 public boolean getUseDownloadCache() { 147 return Boolean.parseBoolean( 148 mProperties.getProperty( 149 ISettingsPage.KEY_USE_DOWNLOAD_CACHE, 150 Boolean.TRUE.toString())); 151 } 152 153 /** 154 * Returns the value of the {@link ISettingsPage#KEY_SHOW_UPDATE_ONLY} setting. 155 * 156 * @see ISettingsPage#KEY_SHOW_UPDATE_ONLY 157 */ 158 public boolean getShowUpdateOnly() { 159 return Boolean.parseBoolean( 160 mProperties.getProperty( 161 ISettingsPage.KEY_SHOW_UPDATE_ONLY, 162 Boolean.TRUE.toString())); 163 } 164 165 /** 166 * Returns the value of the {@link ISettingsPage#KEY_ENABLE_PREVIEWS} setting. 167 * 168 * @see ISettingsPage#KEY_ENABLE_PREVIEWS 169 */ 170 public boolean getEnablePreviews() { 171 return Boolean.parseBoolean(mProperties.getProperty(ISettingsPage.KEY_ENABLE_PREVIEWS)); 172 } 173 174 /** 175 * Returns the value of the {@link ISettingsPage#KEY_MONITOR_DENSITY} setting 176 * @see ISettingsPage#KEY_MONITOR_DENSITY 177 */ 178 public int getMonitorDensity() { 179 String value = mProperties.getProperty(ISettingsPage.KEY_MONITOR_DENSITY, null); 180 if (value == null) { 181 return -1; 182 } 183 184 try { 185 return Integer.parseInt(value); 186 } catch (NumberFormatException e) { 187 return -1; 188 } 189 } 190 } 191 192 /** 193 * Sets the value of the {@link ISettingsPage#KEY_SHOW_UPDATE_ONLY} setting. 194 * 195 * @param enabled True if only compatible non-obsolete update items should be shown. 196 * @see ISettingsPage#KEY_SHOW_UPDATE_ONLY 197 */ 198 public void setShowUpdateOnly(boolean enabled) { 199 setSetting(ISettingsPage.KEY_SHOW_UPDATE_ONLY, enabled); 200 } 201 202 /** 203 * Sets the value of the {@link ISettingsPage#KEY_MONITOR_DENSITY} setting. 204 * 205 * @param density the density of the monitor 206 * @see ISettingsPage#KEY_MONITOR_DENSITY 207 */ 208 public void setMonitorDensity(int density) { 209 mSettings.mProperties.setProperty( 210 ISettingsPage.KEY_MONITOR_DENSITY, Integer.toString(density)); 211 } 212 213 /** 214 * Internal helper to set a boolean setting. 215 */ 216 void setSetting(String key, boolean value) { 217 mSettings.mProperties.setProperty(key, Boolean.toString(value)); 218 } 219 220 //--- Controller methods ------------- 221 222 /** 223 * Associate the given {@link ISettingsPage} with this {@link SettingsController}. 224 * <p/> 225 * This loads the current properties into the setting page UI. 226 * It then associates the SettingsChanged callback with this controller. 227 * <p/> 228 * If the setting page given is null, it will be unlinked from controller. 229 * 230 * @param settingsPage An {@link ISettingsPage} to associate with the controller. 231 */ 232 public void setSettingsPage(ISettingsPage settingsPage) { 233 mSettingsPage = settingsPage; 234 235 if (settingsPage != null) { 236 settingsPage.loadSettings(mSettings.mProperties); 237 238 settingsPage.setOnSettingsChanged(new ISettingsPage.SettingsChangedCallback() { 239 @Override 240 public void onSettingsChanged(ISettingsPage page) { 241 SettingsController.this.onSettingsChanged(); 242 } 243 }); 244 } 245 } 246 247 /** 248 * Load settings from the settings file. 249 */ 250 public void loadSettings() { 251 FileInputStream fis = null; 252 String path = null; 253 try { 254 String folder = AndroidLocation.getFolder(); 255 File f = new File(folder, SETTINGS_FILENAME); 256 path = f.getPath(); 257 if (f.exists()) { 258 fis = new FileInputStream(f); 259 260 mSettings.mProperties.load(fis); 261 262 // Properly reformat some settings to enforce their default value when missing. 263 setShowUpdateOnly(mSettings.getShowUpdateOnly()); 264 setSetting(ISettingsPage.KEY_ASK_ADB_RESTART, mSettings.getAskBeforeAdbRestart()); 265 setSetting(ISettingsPage.KEY_USE_DOWNLOAD_CACHE, mSettings.getUseDownloadCache()); 266 } 267 268 } catch (Exception e) { 269 if (mSdkLog != null) { 270 mSdkLog.error(e, 271 "Failed to load settings from .android folder. Path is '%1$s'.", 272 path); 273 } 274 } finally { 275 if (fis != null) { 276 try { 277 fis.close(); 278 } catch (IOException e) { 279 } 280 } 281 } 282 } 283 284 /** 285 * Saves settings to the settings file. 286 */ 287 public void saveSettings() { 288 289 FileOutputStream fos = null; 290 String path = null; 291 try { 292 String folder = AndroidLocation.getFolder(); 293 File f = new File(folder, SETTINGS_FILENAME); 294 path = f.getPath(); 295 296 fos = new FileOutputStream(f); 297 298 mSettings.mProperties.store(fos, "## Settings for Android Tool"); //$NON-NLS-1$ 299 300 } catch (Exception e) { 301 if (mSdkLog != null) { 302 // This is important enough that we want to really nag the user about it 303 String reason = null; 304 305 if (e instanceof FileNotFoundException) { 306 reason = "File not found"; 307 } else if (e instanceof AndroidLocationException) { 308 reason = ".android folder not found, please define ANDROID_SDK_HOME"; 309 } else if (e.getMessage() != null) { 310 reason = String.format("%1$s: %2$s", 311 e.getClass().getSimpleName(), 312 e.getMessage()); 313 } else { 314 reason = e.getClass().getName(); 315 } 316 317 mSdkLog.error(e, "Failed to save settings file '%1$s': %2$s", path, reason); 318 } 319 } finally { 320 if (fos != null) { 321 try { 322 fos.close(); 323 } catch (IOException e) { 324 } 325 } 326 } 327 } 328 329 /** 330 * When settings have changed: retrieve the new settings, apply them, save them 331 * and notify on-settings-changed listeners. 332 */ 333 private void onSettingsChanged() { 334 if (mSettingsPage == null) { 335 return; 336 } 337 338 Settings oldSettings = new Settings(mSettings); 339 mSettingsPage.retrieveSettings(mSettings.mProperties); 340 applySettings(); 341 saveSettings(); 342 343 for (OnChangedListener listener : mChangedListeners) { 344 try { 345 listener.onSettingsChanged(this, oldSettings); 346 } catch (Throwable ignore) {} 347 } 348 } 349 350 /** 351 * Applies the current settings. 352 */ 353 public void applySettings() { 354 Properties props = System.getProperties(); 355 356 // Get the configured HTTP proxy settings 357 String proxyHost = mSettings.mProperties.getProperty(ISettingsPage.KEY_HTTP_PROXY_HOST, 358 ""); //$NON-NLS-1$ 359 String proxyPort = mSettings.mProperties.getProperty(ISettingsPage.KEY_HTTP_PROXY_PORT, 360 ""); //$NON-NLS-1$ 361 362 // Set both the HTTP and HTTPS proxy system properties. 363 // The system property constants can be found in the Java SE documentation at 364 // http://download.oracle.com/javase/6/docs/technotes/guides/net/proxies.html 365 final String JAVA_PROP_HTTP_PROXY_HOST = "http.proxyHost"; //$NON-NLS-1$ 366 final String JAVA_PROP_HTTP_PROXY_PORT = "http.proxyPort"; //$NON-NLS-1$ 367 final String JAVA_PROP_HTTPS_PROXY_HOST = "https.proxyHost"; //$NON-NLS-1$ 368 final String JAVA_PROP_HTTPS_PROXY_PORT = "https.proxyPort"; //$NON-NLS-1$ 369 370 // Only change the proxy if have something in the preferences. 371 // Do not erase the default settings by empty values. 372 if (proxyHost != null && proxyHost.length() > 0) { 373 props.setProperty(JAVA_PROP_HTTP_PROXY_HOST, proxyHost); 374 props.setProperty(JAVA_PROP_HTTPS_PROXY_HOST, proxyHost); 375 } 376 if (proxyPort != null && proxyPort.length() > 0) { 377 props.setProperty(JAVA_PROP_HTTP_PROXY_PORT, proxyPort); 378 props.setProperty(JAVA_PROP_HTTPS_PROXY_PORT, proxyPort); 379 } 380 } 381 382 } 383