Home | History | Annotate | Download | only in repository
      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