/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.sdklib.internal.repository.updater; import com.android.annotations.NonNull; import com.android.annotations.Nullable; import com.android.annotations.VisibleForTesting; import com.android.annotations.VisibleForTesting.Visibility; import com.android.prefs.AndroidLocation; import com.android.prefs.AndroidLocation.AndroidLocationException; import com.android.sdklib.io.FileOp; import com.android.sdklib.io.IFileOp; import com.android.utils.ILogger; import java.io.File; import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.List; import java.util.Map.Entry; import java.util.Properties; /** * Controller class to get settings values. Settings are kept in-memory. * Users of this class must first load the settings before changing them and save * them when modified. * <p/> * Settings are enumerated by constants in {@link ISettingsPage}. * * @deprecated * com.android.sdklib.internal.repository has moved into Studio as * com.android.tools.idea.sdk.remote.internal. */ @Deprecated public class SettingsController { private static final String SETTINGS_FILENAME = "androidtool.cfg"; //$NON-NLS-1$ private final IFileOp mFileOp; private final ILogger mSdkLog; private final Settings mSettings; public interface OnChangedListener { void onSettingsChanged(@NonNull SettingsController controller, @NonNull Settings oldSettings); } private final List<OnChangedListener> mChangedListeners = new ArrayList<OnChangedListener>(1); /** The currently associated {@link ISettingsPage}. Can be null. */ private ISettingsPage mSettingsPage; /** * Constructs a new default {@link SettingsController}. * * @param sdkLog A non-null logger to use. */ public SettingsController(@NonNull ILogger sdkLog) { this(new FileOp(), sdkLog); } /** * Constructs a new default {@link SettingsController}. * * @param fileOp A non-null {@link FileOp} to perform file operations (to load/save settings.) * @param sdkLog A non-null logger to use. */ public SettingsController(@NonNull IFileOp fileOp, @NonNull ILogger sdkLog) { this(fileOp, sdkLog, new Settings()); } /** * Specialized constructor that wraps an existing {@link Settings} instance. * This is mostly used in unit-tests to override settings that are being used. * Normal usage should NOT need to call this constructor. * * @param fileOp A non-null {@link FileOp} to perform file operations (to load/save settings) * @param sdkLog A non-null logger to use. * @param settings A non-null {@link Settings} to use as-is. It is not duplicated. */ @VisibleForTesting(visibility=Visibility.PRIVATE) protected SettingsController(@NonNull IFileOp fileOp, @NonNull ILogger sdkLog, @NonNull Settings settings) { mFileOp = fileOp; mSdkLog = sdkLog; mSettings = settings; } @NonNull public Settings getSettings() { return mSettings; } public void registerOnChangedListener(@Nullable OnChangedListener listener) { if (listener != null && !mChangedListeners.contains(listener)) { mChangedListeners.add(listener); } } public void unregisterOnChangedListener(@Nullable OnChangedListener listener) { if (listener != null) { mChangedListeners.remove(listener); } } //--- Access to settings ------------ public static class Settings { private final Properties mProperties; /** Initialize an empty set of settings. */ public Settings() { mProperties = new Properties(); } /** Duplicates a set of settings. */ public Settings(@NonNull Settings settings) { this(); for (Entry<Object, Object> entry : settings.mProperties.entrySet()) { mProperties.put(entry.getKey(), entry.getValue()); } } /** * Specialized constructor for unit-tests that wraps an existing * {@link Properties} instance. The properties instance is not duplicated, * it's merely used as-is and changes will be reflected directly. */ protected Settings(@NonNull Properties properties) { mProperties = properties; } /** * Returns the value of the {@link ISettingsPage#KEY_FORCE_HTTP} setting. * * @see ISettingsPage#KEY_FORCE_HTTP */ public boolean getForceHttp() { return Boolean.parseBoolean(mProperties.getProperty(ISettingsPage.KEY_FORCE_HTTP)); } /** * Returns the value of the {@link ISettingsPage#KEY_ASK_ADB_RESTART} setting. * * @see ISettingsPage#KEY_ASK_ADB_RESTART */ public boolean getAskBeforeAdbRestart() { return Boolean.parseBoolean(mProperties.getProperty(ISettingsPage.KEY_ASK_ADB_RESTART)); } /** * Returns the value of the {@link ISettingsPage#KEY_USE_DOWNLOAD_CACHE} setting. * * @see ISettingsPage#KEY_USE_DOWNLOAD_CACHE */ public boolean getUseDownloadCache() { return Boolean.parseBoolean( mProperties.getProperty( ISettingsPage.KEY_USE_DOWNLOAD_CACHE, Boolean.TRUE.toString())); } /** * Returns the value of the {@link ISettingsPage#KEY_SHOW_UPDATE_ONLY} setting. * * @see ISettingsPage#KEY_SHOW_UPDATE_ONLY */ public boolean getShowUpdateOnly() { return Boolean.parseBoolean( mProperties.getProperty( ISettingsPage.KEY_SHOW_UPDATE_ONLY, Boolean.TRUE.toString())); } /** * Returns the value of the {@link ISettingsPage#KEY_ENABLE_PREVIEWS} setting. * * @see ISettingsPage#KEY_ENABLE_PREVIEWS */ public boolean getEnablePreviews() { return Boolean.parseBoolean( mProperties.getProperty( ISettingsPage.KEY_ENABLE_PREVIEWS, Boolean.TRUE.toString())); } /** * Returns the value of the {@link ISettingsPage#KEY_MONITOR_DENSITY} setting * @see ISettingsPage#KEY_MONITOR_DENSITY */ public int getMonitorDensity() { String value = mProperties.getProperty(ISettingsPage.KEY_MONITOR_DENSITY, null); if (value == null) { return -1; } try { return Integer.parseInt(value); } catch (NumberFormatException e) { return -1; } } } /** * Sets the value of the {@link ISettingsPage#KEY_SHOW_UPDATE_ONLY} setting. * * @param enabled True if only compatible non-obsolete update items should be shown. * @see ISettingsPage#KEY_SHOW_UPDATE_ONLY */ public void setShowUpdateOnly(boolean enabled) { setSetting(ISettingsPage.KEY_SHOW_UPDATE_ONLY, enabled); } /** * Sets the value of the {@link ISettingsPage#KEY_MONITOR_DENSITY} setting. * * @param density the density of the monitor * @see ISettingsPage#KEY_MONITOR_DENSITY */ public void setMonitorDensity(int density) { mSettings.mProperties.setProperty( ISettingsPage.KEY_MONITOR_DENSITY, Integer.toString(density)); } /** * Internal helper to set a boolean setting. */ void setSetting(@NonNull String key, boolean value) { mSettings.mProperties.setProperty(key, Boolean.toString(value)); } //--- Controller methods ------------- /** * Associate the given {@link ISettingsPage} with this {@link SettingsController}. * <p/> * This loads the current properties into the setting page UI. * It then associates the SettingsChanged callback with this controller. * <p/> * If the setting page given is null, it will be unlinked from controller. * * @param settingsPage An {@link ISettingsPage} to associate with the controller. */ public void setSettingsPage(@Nullable ISettingsPage settingsPage) { mSettingsPage = settingsPage; if (settingsPage != null) { settingsPage.loadSettings(mSettings.mProperties); settingsPage.setOnSettingsChanged(new ISettingsPage.SettingsChangedCallback() { @Override public void onSettingsChanged(ISettingsPage page) { SettingsController.this.onSettingsChanged(); } }); } } /** * Load settings from the settings file. */ public void loadSettings() { String path = null; try { String folder = AndroidLocation.getFolder(); File f = new File(folder, SETTINGS_FILENAME); path = f.getPath(); Properties props = mFileOp.loadProperties(f); mSettings.mProperties.clear(); mSettings.mProperties.putAll(props); // Properly reformat some settings to enforce their default value when missing. setShowUpdateOnly(mSettings.getShowUpdateOnly()); setSetting(ISettingsPage.KEY_ASK_ADB_RESTART, mSettings.getAskBeforeAdbRestart()); setSetting(ISettingsPage.KEY_USE_DOWNLOAD_CACHE, mSettings.getUseDownloadCache()); setSetting(ISettingsPage.KEY_ENABLE_PREVIEWS, mSettings.getEnablePreviews()); } catch (Exception e) { if (mSdkLog != null) { mSdkLog.error(e, "Failed to load settings from .android folder. Path is '%1$s'.", path); } } } /** * Saves settings to the settings file. */ public void saveSettings() { String path = null; try { String folder = AndroidLocation.getFolder(); File f = new File(folder, SETTINGS_FILENAME); path = f.getPath(); mFileOp.saveProperties(f, mSettings.mProperties, "## Settings for Android Tool"); //$NON-NLS-1$ } catch (Exception e) { if (mSdkLog != null) { // This is important enough that we want to really nag the user about it String reason = null; if (e instanceof FileNotFoundException) { reason = "File not found"; } else if (e instanceof AndroidLocationException) { reason = ".android folder not found, please define ANDROID_SDK_HOME"; } else if (e.getMessage() != null) { reason = String.format("%1$s: %2$s", e.getClass().getSimpleName(), e.getMessage()); } else { reason = e.getClass().getName(); } mSdkLog.error(e, "Failed to save settings file '%1$s': %2$s", path, reason); } } } /** * When settings have changed: retrieve the new settings, apply them, save them * and notify on-settings-changed listeners. */ private void onSettingsChanged() { if (mSettingsPage == null) { return; } Settings oldSettings = new Settings(mSettings); mSettingsPage.retrieveSettings(mSettings.mProperties); applySettings(); saveSettings(); for (OnChangedListener listener : mChangedListeners) { try { listener.onSettingsChanged(this, oldSettings); } catch (Throwable ignore) {} } } /** * Applies the current settings. */ public void applySettings() { Properties props = System.getProperties(); // Get the configured HTTP proxy settings String proxyHost = mSettings.mProperties.getProperty(ISettingsPage.KEY_HTTP_PROXY_HOST, ""); //$NON-NLS-1$ String proxyPort = mSettings.mProperties.getProperty(ISettingsPage.KEY_HTTP_PROXY_PORT, ""); //$NON-NLS-1$ // Set both the HTTP and HTTPS proxy system properties. // The system property constants can be found in the Java SE documentation at // http://download.oracle.com/javase/6/docs/technotes/guides/net/proxies.html final String JAVA_PROP_HTTP_PROXY_HOST = "http.proxyHost"; //$NON-NLS-1$ final String JAVA_PROP_HTTP_PROXY_PORT = "http.proxyPort"; //$NON-NLS-1$ final String JAVA_PROP_HTTPS_PROXY_HOST = "https.proxyHost"; //$NON-NLS-1$ final String JAVA_PROP_HTTPS_PROXY_PORT = "https.proxyPort"; //$NON-NLS-1$ // Only change the proxy if have something in the preferences. // Do not erase the default settings by empty values. if (proxyHost != null && !proxyHost.isEmpty()) { props.setProperty(JAVA_PROP_HTTP_PROXY_HOST, proxyHost); props.setProperty(JAVA_PROP_HTTPS_PROXY_HOST, proxyHost); } if (proxyPort != null && !proxyPort.isEmpty()) { props.setProperty(JAVA_PROP_HTTP_PROXY_PORT, proxyPort); props.setProperty(JAVA_PROP_HTTPS_PROXY_PORT, proxyPort); } } }