private Text mProxyServerText;\r
private Text mProxyPortText;\r
private Button mForceHttpCheck;\r
+ private Button mAskAdbRestartCheck;\r
\r
private ModifyListener mSetApplyDirty = new ModifyListener() {\r
public void modifyText(ModifyEvent e) {\r
mProxyServerLabel = new Label(mProxySettingsGroup, SWT.NONE);\r
mProxyServerLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));\r
mProxyServerLabel.setText("HTTP Proxy Server");\r
+ String tooltip = "The DNS name or IP of the HTTP proxy server to use. " +\r
+ "When empty, no HTTP proxy is used.";\r
+ mProxyServerLabel.setToolTipText(tooltip);\r
\r
mProxyServerText = new Text(mProxySettingsGroup, SWT.BORDER);\r
mProxyServerText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));\r
mProxyServerText.addModifyListener(mSetApplyDirty);\r
+ mProxyServerText.setToolTipText(tooltip);\r
\r
mProxyPortLabel = new Label(mProxySettingsGroup, SWT.NONE);\r
mProxyPortLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));\r
mProxyPortLabel.setText("HTTP Proxy Port");\r
+ tooltip = "The port of the HTTP proxy server to use. " +\r
+ "When empty, the default for HTTP or HTTPS is used.";\r
+ mProxyPortLabel.setToolTipText(tooltip);\r
\r
mProxyPortText = new Text(mProxySettingsGroup, SWT.BORDER);\r
mProxyPortText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));\r
mProxyPortText.addModifyListener(mSetApplyDirty);\r
+ mProxyPortText.setToolTipText(tooltip);\r
\r
mMiscGroup = new Group(this, SWT.NONE);\r
mMiscGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));\r
mMiscGroup.setLayout(new GridLayout(2, false));\r
\r
mForceHttpCheck = new Button(mMiscGroup, SWT.CHECK);\r
+ mForceHttpCheck.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));\r
mForceHttpCheck.setText("Force https://... sources to be fetched using http://...");\r
+ mForceHttpCheck.setToolTipText("If you are not able to connect to the official Android repository " +\r
+ "using HTTPS, enable this setting to force accessing it via HTTP.");\r
mForceHttpCheck.addSelectionListener(new SelectionAdapter() {\r
@Override\r
public void widgetSelected(SelectionEvent e) {\r
}\r
});\r
\r
+ mAskAdbRestartCheck = new Button(mMiscGroup, SWT.CHECK);\r
+ mAskAdbRestartCheck.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));\r
+ mAskAdbRestartCheck.setText("Ask before restarting ADB");\r
+ mAskAdbRestartCheck.setToolTipText("When checked, the user will be asked for permission " +\r
+ "to restart ADB after updating an addon-on package or a tool package.");\r
+ mAskAdbRestartCheck.addSelectionListener(new SelectionAdapter() {\r
+ @Override\r
+ public void widgetSelected(SelectionEvent e) {\r
+ onForceHttpSelected(); //$hide$\r
+ }\r
+ });\r
+\r
mApplyButton = new Button(this, SWT.NONE);\r
mApplyButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));\r
mApplyButton.setText("Save && Apply");\r
mProxyServerText.setText(in_settings.getProperty(KEY_HTTP_PROXY_HOST, "")); //$NON-NLS-1$\r
mProxyPortText.setText( in_settings.getProperty(KEY_HTTP_PROXY_PORT, "")); //$NON-NLS-1$\r
mForceHttpCheck.setSelection(Boolean.parseBoolean(in_settings.getProperty(KEY_FORCE_HTTP)));\r
+ mAskAdbRestartCheck.setSelection(Boolean.parseBoolean(in_settings.getProperty(KEY_ASK_ADB_RESTART)));\r
\r
// We loaded fresh settings so there's nothing dirty to apply\r
mApplyButton.setEnabled(false);\r
out_settings.setProperty(KEY_HTTP_PROXY_PORT, mProxyPortText.getText());\r
out_settings.setProperty(KEY_FORCE_HTTP,\r
Boolean.toString(mForceHttpCheck.getSelection()));\r
+ out_settings.setProperty(KEY_ASK_ADB_RESTART,\r
+ Boolean.toString(mAskAdbRestartCheck.getSelection()));\r
}\r
\r
/**\r
--- /dev/null
+/*\r
+ * Copyright (C) 2009 The Android Open Source Project\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+package com.android.sdkuilib.internal.repository;\r
+\r
+import com.android.sdklib.SdkConstants;\r
+import com.android.sdklib.internal.repository.ITaskMonitor;\r
+\r
+import java.io.BufferedReader;\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.io.InputStreamReader;\r
+import java.util.ArrayList;\r
+\r
+/**\r
+ * A lightweight wrapper to start & stop ADB.\r
+ */\r
+public class AdbWrapper {\r
+\r
+ /*\r
+ * Note: we could bring ddmlib in SdkManager for that purpose, however this allows us to\r
+ * specialize the start/stop methods to our needs (e.g. a task monitor, etc.)\r
+ */\r
+\r
+ private final String mAdbOsLocation;\r
+ private final ITaskMonitor mMonitor;\r
+\r
+ /**\r
+ * Creates a new lightweight ADB wrapper.\r
+ *\r
+ * @param osSdkPath The root OS path of the SDK. Cannot be null.\r
+ * @param monitor A logger object. Cannot be null.\r
+ */\r
+ public AdbWrapper(String osSdkPath, ITaskMonitor monitor) {\r
+ mMonitor = monitor;\r
+\r
+ if (!osSdkPath.endsWith(File.separator)) {\r
+ osSdkPath += File.separator;\r
+ }\r
+ mAdbOsLocation = osSdkPath + SdkConstants.OS_SDK_TOOLS_FOLDER + SdkConstants.FN_ADB;\r
+ }\r
+\r
+ private void display(String format, Object...args) {\r
+ mMonitor.setResult(format, args);\r
+ }\r
+\r
+ /**\r
+ * Starts the adb host side server.\r
+ * @return true if success\r
+ */\r
+ public synchronized boolean startAdb() {\r
+ if (mAdbOsLocation == null) {\r
+ display("Error: missing path to ADB."); //$NON-NLS-1$\r
+ return false;\r
+ }\r
+\r
+ Process proc;\r
+ int status = -1;\r
+\r
+ try {\r
+ String[] command = new String[2];\r
+ command[0] = mAdbOsLocation;\r
+ command[1] = "start-server"; //$NON-NLS-1$\r
+ proc = Runtime.getRuntime().exec(command);\r
+\r
+ ArrayList<String> errorOutput = new ArrayList<String>();\r
+ ArrayList<String> stdOutput = new ArrayList<String>();\r
+ status = grabProcessOutput(proc, errorOutput, stdOutput,\r
+ false /* waitForReaders */);\r
+\r
+ } catch (IOException ioe) {\r
+ display("Unable to run 'adb': %1$s.", ioe.getMessage()); //$NON-NLS-1$\r
+ // we'll return false;\r
+ } catch (InterruptedException ie) {\r
+ display("Unable to run 'adb': %1$s.", ie.getMessage()); //$NON-NLS-1$\r
+ // we'll return false;\r
+ }\r
+\r
+ if (status != 0) {\r
+ display("'adb start-server' failed."); //$NON-NLS-1$\r
+ return false;\r
+ }\r
+\r
+ display("'adb start-server' succeeded."); //$NON-NLS-1$\r
+\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * Stops the adb host side server.\r
+ * @return true if success\r
+ */\r
+ public synchronized boolean stopAdb() {\r
+ if (mAdbOsLocation == null) {\r
+ display("Error: missing path to ADB."); //$NON-NLS-1$\r
+ return false;\r
+ }\r
+\r
+ Process proc;\r
+ int status = -1;\r
+\r
+ try {\r
+ String[] command = new String[2];\r
+ command[0] = mAdbOsLocation;\r
+ command[1] = "kill-server"; //$NON-NLS-1$\r
+ proc = Runtime.getRuntime().exec(command);\r
+ status = proc.waitFor();\r
+ }\r
+ catch (IOException ioe) {\r
+ // we'll return false;\r
+ }\r
+ catch (InterruptedException ie) {\r
+ // we'll return false;\r
+ }\r
+\r
+ if (status != 0) {\r
+ display("'adb kill-server' failed -- run manually if necessary."); //$NON-NLS-1$\r
+ return false;\r
+ }\r
+\r
+ display("'adb kill-server' succeeded."); //$NON-NLS-1$\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * Get the stderr/stdout outputs of a process and return when the process is done.\r
+ * Both <b>must</b> be read or the process will block on windows.\r
+ * @param process The process to get the ouput from\r
+ * @param errorOutput The array to store the stderr output. cannot be null.\r
+ * @param stdOutput The array to store the stdout output. cannot be null.\r
+ * @param waitforReaders if true, this will wait for the reader threads.\r
+ * @return the process return code.\r
+ * @throws InterruptedException\r
+ */\r
+ private int grabProcessOutput(final Process process, final ArrayList<String> errorOutput,\r
+ final ArrayList<String> stdOutput, boolean waitforReaders)\r
+ throws InterruptedException {\r
+ assert errorOutput != null;\r
+ assert stdOutput != null;\r
+ // read the lines as they come. if null is returned, it's\r
+ // because the process finished\r
+ Thread t1 = new Thread("") { //$NON-NLS-1$\r
+ @Override\r
+ public void run() {\r
+ // create a buffer to read the stderr output\r
+ InputStreamReader is = new InputStreamReader(process.getErrorStream());\r
+ BufferedReader errReader = new BufferedReader(is);\r
+\r
+ try {\r
+ while (true) {\r
+ String line = errReader.readLine();\r
+ if (line != null) {\r
+ display("ADB Error: %1$s", line);\r
+ errorOutput.add(line);\r
+ } else {\r
+ break;\r
+ }\r
+ }\r
+ } catch (IOException e) {\r
+ // do nothing.\r
+ }\r
+ }\r
+ };\r
+\r
+ Thread t2 = new Thread("") { //$NON-NLS-1$\r
+ @Override\r
+ public void run() {\r
+ InputStreamReader is = new InputStreamReader(process.getInputStream());\r
+ BufferedReader outReader = new BufferedReader(is);\r
+\r
+ try {\r
+ while (true) {\r
+ String line = outReader.readLine();\r
+ if (line != null) {\r
+ display("ADB: %1$s", line);\r
+ stdOutput.add(line);\r
+ } else {\r
+ break;\r
+ }\r
+ }\r
+ } catch (IOException e) {\r
+ // do nothing.\r
+ }\r
+ }\r
+ };\r
+\r
+ t1.start();\r
+ t2.start();\r
+\r
+ // it looks like on windows process#waitFor() can return\r
+ // before the thread have filled the arrays, so we wait for both threads and the\r
+ // process itself.\r
+ if (waitforReaders) {\r
+ try {\r
+ t1.join();\r
+ } catch (InterruptedException e) {\r
+ }\r
+ try {\r
+ t2.join();\r
+ } catch (InterruptedException e) {\r
+ }\r
+ }\r
+\r
+ // get the return code from the process\r
+ return process.waitFor();\r
+ }\r
+\r
+}\r
*/\r
public interface ISettingsPage {\r
\r
- /** Java system setting picked up by {@link URL} for http proxy port. Type: String. */\r
+ /**\r
+ * Java system setting picked up by {@link URL} for http proxy port.\r
+ * Type: String.\r
+ */\r
public static final String KEY_HTTP_PROXY_PORT = "http.proxyPort"; //$NON-NLS-1$\r
- /** Java system setting picked up by {@link URL} for http proxy host. Type: String. */\r
+ /**\r
+ * Java system setting picked up by {@link URL} for http proxy host.\r
+ * Type: String.\r
+ */\r
public static final String KEY_HTTP_PROXY_HOST = "http.proxyHost"; //$NON-NLS-1$\r
- /** Setting to force using http:// instead of https:// connections. Type: Boolean. */\r
+ /**\r
+ * Setting to force using http:// instead of https:// connections.\r
+ * Type: Boolean.\r
+ * Default: False.\r
+ */\r
public static final String KEY_FORCE_HTTP = "sdkman.force.http"; //$NON-NLS-1$\r
- /** Setting to display only packages that are new or updates. Type: Boolean. */\r
+ /**\r
+ * Setting to display only packages that are new or updates.\r
+ * Type: Boolean.\r
+ * Default: True.\r
+ */\r
public static final String KEY_SHOW_UPDATE_ONLY = "sdkman.show.update.only"; //$NON-NLS-1$\r
+ /**\r
+ * Setting to ask for permission before restarting ADB.\r
+ * Type: Boolean.\r
+ * Default: True.\r
+ */\r
+ public static final String KEY_ASK_ADB_RESTART = "sdkman.ask.adb.restart"; //$NON-NLS-1$\r
\r
/** Loads settings from the given {@link Properties} container and update the page UI. */\r
public abstract void loadSettings(Properties in_settings);\r
import org.eclipse.swt.events.ControlEvent;\r
import org.eclipse.swt.events.SelectionAdapter;\r
import org.eclipse.swt.events.SelectionEvent;\r
-import org.eclipse.swt.graphics.Color;\r
import org.eclipse.swt.graphics.Rectangle;\r
import org.eclipse.swt.layout.GridData;\r
import org.eclipse.swt.layout.GridLayout;\r
import org.eclipse.swt.widgets.Button;\r
import org.eclipse.swt.widgets.Composite;\r
-import org.eclipse.swt.widgets.Display;\r
import org.eclipse.swt.widgets.Group;\r
import org.eclipse.swt.widgets.Label;\r
import org.eclipse.swt.widgets.Tree;\r
import java.util.Properties;\r
\r
/**\r
- *\r
+ * Controller class to get settings values. Settings are kept in-memory.\r
+ * Users of this class must first load the settings before changing them and save\r
+ * them when modified.\r
+ * <p/>\r
+ * Settings are enumerated by constants in {@link ISettingsPage}.\r
*/\r
public class SettingsController {\r
\r
\r
//--- Access to settings ------------\r
\r
+ /**\r
+ * Returns the value of the ISettingsPage#KEY_FORCE_HTTP setting.\r
+ * @see ISettingsPage#KEY_FORCE_HTTP\r
+ */\r
public boolean getForceHttp() {\r
return Boolean.parseBoolean(mProperties.getProperty(ISettingsPage.KEY_FORCE_HTTP));\r
}\r
\r
+ /**\r
+ * Returns the value of the ISettingsPage#KEY_ASK_ADB_RESTART setting.\r
+ * @see ISettingsPage#KEY_ASK_ADB_RESTART\r
+ */\r
+ public boolean getAskBeforeAdbRestart() {\r
+ String value = mProperties.getProperty(ISettingsPage.KEY_ASK_ADB_RESTART);\r
+ if (value == null) {\r
+ return true;\r
+ }\r
+ return Boolean.parseBoolean(value);\r
+ }\r
+\r
+ /**\r
+ * Returns the value of the ISettingsPage#KEY_SHOW_UPDATE_ONLY setting.\r
+ * @see ISettingsPage#KEY_SHOW_UPDATE_ONLY\r
+ */\r
public boolean getShowUpdateOnly() {\r
String value = mProperties.getProperty(ISettingsPage.KEY_SHOW_UPDATE_ONLY);\r
if (value == null) {\r
return Boolean.parseBoolean(value);\r
}\r
\r
+ /**\r
+ * Sets the value of the ISettingsPage#KEY_SHOW_UPDATE_ONLY setting.\r
+ * @param enabled True if only compatible update items should be shown.\r
+ * @see ISettingsPage#KEY_SHOW_UPDATE_ONLY\r
+ */\r
public void setShowUpdateOnly(boolean enabled) {\r
- mProperties.setProperty(ISettingsPage.KEY_SHOW_UPDATE_ONLY, Boolean.toString(enabled));\r
+ setSetting(ISettingsPage.KEY_SHOW_UPDATE_ONLY, enabled);\r
+ }\r
+\r
+ /**\r
+ * Internal helper to set a boolean setting.\r
+ */\r
+ private void setSetting(String key, boolean value) {\r
+ mProperties.setProperty(key, Boolean.toString(value));\r
}\r
\r
//--- Controller methods -------------\r
fis = new FileInputStream(f);\r
\r
mProperties.load(fis);\r
+\r
+ // Properly reformat some settings to enforce their default value when missing.\r
+ setShowUpdateOnly(getShowUpdateOnly());\r
+ setSetting(ISettingsPage.KEY_ASK_ADB_RESTART, getAskBeforeAdbRestart());\r
}\r
\r
} catch (AndroidLocationException e) {\r
import com.android.sdkuilib.internal.repository.icons.ImageFactory;\r
import com.android.sdkuilib.repository.UpdaterWindow.ISdkListener;\r
\r
+import org.eclipse.jface.dialogs.MessageDialog;\r
+import org.eclipse.swt.widgets.Display;\r
import org.eclipse.swt.widgets.Shell;\r
\r
import java.io.ByteArrayOutputStream;\r
break;\r
}\r
\r
- if (archive.getParentPackage() instanceof AddonPackage) {\r
- installedAddon = true;\r
- } else if (archive.getParentPackage() instanceof ToolPackage) {\r
- installedTools = true;\r
- }\r
-\r
if (archive.install(mOsSdkRoot, forceHttp, mSdkManager, monitor)) {\r
numInstalled++;\r
+\r
+ // Check if we successfully installed a tool or add-on package.\r
+ if (archive.getParentPackage() instanceof AddonPackage) {\r
+ installedAddon = true;\r
+ } else if (archive.getParentPackage() instanceof ToolPackage) {\r
+ installedTools = true;\r
+ }\r
}\r
\r
} catch (Throwable t) {\r
// Update the USB vendor ids for adb\r
try {\r
mSdkManager.updateAdb();\r
+ monitor.setResult("Updated ADB to support the USB devices declared in the SDK add-ons.");\r
} catch (Exception e) {\r
mSdkLog.error(e, "Update ADB failed");\r
+ monitor.setResult("failed to update adb to support the USB devices declared in the SDK add-ons.");\r
}\r
}\r
\r
// before updating the tools folder, as adb.exe is (surprisingly) not\r
// locked.\r
\r
- // TODO either bring in ddmlib and use its existing methods to stop adb\r
- // or use a shell exec to tools/adb.\r
+ askForAdbRestart(monitor);\r
+ }\r
+\r
+ if (installedTools) {\r
+ notifyToolsNeedsToBeRestarted();\r
}\r
\r
if (numInstalled == 0) {\r
}\r
\r
/**\r
+ * Attemps to restart ADB.\r
+ *\r
+ * If the "ask before restart" setting is set (the default), prompt the user whether\r
+ * now is a good time to restart ADB.\r
+ * @param monitor\r
+ */\r
+ private void askForAdbRestart(ITaskMonitor monitor) {\r
+ final boolean[] canRestart = new boolean[] { true };\r
+\r
+ if (getSettingsController().getAskBeforeAdbRestart()) {\r
+ // need to ask for permission first\r
+ Display display = mWindowShell.getDisplay();\r
+\r
+ display.syncExec(new Runnable() {\r
+ public void run() {\r
+ canRestart[0] = MessageDialog.openQuestion(mWindowShell,\r
+ "ADB Restart",\r
+ "A package that depends on ADB has been updated. It is recommended " +\r
+ "to restart ADB. Is it OK to do it now? If not, you can restart it " +\r
+ "manually later.");\r
+ }\r
+ });\r
+ }\r
+\r
+ if (canRestart[0]) {\r
+ AdbWrapper adb = new AdbWrapper(getOsSdkRoot(), monitor);\r
+ adb.stopAdb();\r
+ adb.startAdb();\r
+ }\r
+ }\r
+\r
+ private void notifyToolsNeedsToBeRestarted() {\r
+ Display display = mWindowShell.getDisplay();\r
+\r
+ display.syncExec(new Runnable() {\r
+ public void run() {\r
+ MessageDialog.openInformation(mWindowShell,\r
+ "Android Tools Updated",\r
+ "The Android SDK tool that you are currently using has been updated. " +\r
+ "It is recommended that you now close the Android SDK window and re-open it. " +\r
+ "If you started this window from Eclipse, please check if the Android " +\r
+ "plug-in needs to be updated.");\r
+ }\r
+ });\r
+ }\r
+\r
+\r
+ /**\r
* Tries to update all the *existing* local packages.\r
* This first refreshes all sources, then compares the available remote packages with\r
* the current local ones and suggest updates to be done to the user. Finally all\r