aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.ddms/src/com
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.ddms/src/com')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/CommonAction.java70
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/DdmsPlugin.java876
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/IClientAction.java26
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/IDebuggerConnector.java45
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/ISourceRevealer.java43
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/IToolsLocator.java41
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/ITraceviewLauncher.java25
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/JavaSourceRevealer.java83
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/LogCatMonitor.java277
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/LogCatMonitorDialog.java118
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/Perspective.java89
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/editors/UiAutomatorViewer.java172
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/i18n/Messages.java121
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/i18n/messages.properties107
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/preferences/LogCatColorsPage.java65
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/preferences/LogCatPreferencePage.java159
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/preferences/PreferenceInitializer.java155
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/preferences/PreferencePage.java152
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/ISystraceOptions.java25
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/ISystraceOptionsDialog.java23
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceOptionsDialogV1.java445
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceOptionsDialogV2.java369
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceOutputParser.java169
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceTag.java27
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceTask.java101
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceVersionDetector.java100
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/AllocTrackerView.java47
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/DeviceView.java877
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/EmulatorControlView.java42
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/EventLogView.java111
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/FileExplorerView.java181
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/HeapView.java47
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/LogCatView.java120
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/NativeHeapView.java47
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/NetworkStatisticsView.java47
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/OldLogCatView.java385
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/SelectionDependentViewPart.java71
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/SysInfoView.java29
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/TableView.java101
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/ThreadView.java47
40 files changed, 6035 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/CommonAction.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/CommonAction.java
new file mode 100644
index 000000000..ae13037f8
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/CommonAction.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2007 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.ide.eclipse.ddms;
+
+import com.android.ddmuilib.actions.ICommonAction;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.resource.ImageDescriptor;
+
+/**
+ * Basic action extending the jFace Action class in order to implement
+ * ICommonAction.
+ */
+public class CommonAction extends Action implements ICommonAction {
+
+ private Runnable mRunnable;
+
+ public CommonAction() {
+ super();
+ }
+
+ public CommonAction(String text) {
+ super(text);
+ }
+
+ /**
+ * @param text
+ * @param image
+ */
+ public CommonAction(String text, ImageDescriptor image) {
+ super(text, image);
+ }
+
+ /**
+ * @param text
+ * @param style
+ */
+ public CommonAction(String text, int style) {
+ super(text, style);
+ }
+
+ @Override
+ public void run() {
+ if (mRunnable != null) {
+ mRunnable.run();
+ }
+ }
+
+ /**
+ * Sets the {@link Runnable}.
+ * @see ICommonAction#setRunnable(Runnable)
+ */
+ @Override
+ public void setRunnable(Runnable runnable) {
+ mRunnable = runnable;
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/DdmsPlugin.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/DdmsPlugin.java
new file mode 100644
index 000000000..e08080bf0
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/DdmsPlugin.java
@@ -0,0 +1,876 @@
+/*
+ * Copyright (C) 2007 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.ide.eclipse.ddms;
+
+import com.android.annotations.NonNull;
+import com.android.ddmlib.AndroidDebugBridge;
+import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener;
+import com.android.ddmlib.Client;
+import com.android.ddmlib.DdmPreferences;
+import com.android.ddmlib.IDevice;
+import com.android.ddmlib.Log;
+import com.android.ddmlib.Log.ILogOutput;
+import com.android.ddmlib.Log.LogLevel;
+import com.android.ddmuilib.DdmUiPreferences;
+import com.android.ddmuilib.DevicePanel.IUiSelectionListener;
+import com.android.ddmuilib.StackTracePanel;
+import com.android.ddmuilib.console.DdmConsole;
+import com.android.ddmuilib.console.IDdmConsole;
+import com.android.ide.eclipse.ddms.i18n.Messages;
+import com.android.ide.eclipse.ddms.preferences.PreferenceInitializer;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.console.ConsolePlugin;
+import org.eclipse.ui.console.IConsole;
+import org.eclipse.ui.console.MessageConsole;
+import org.eclipse.ui.console.MessageConsoleStream;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public final class DdmsPlugin extends AbstractUIPlugin implements IDeviceChangeListener,
+ IUiSelectionListener, com.android.ddmuilib.StackTracePanel.ISourceRevealer {
+
+
+ // The plug-in ID
+ public static final String PLUGIN_ID = "com.android.ide.eclipse.ddms"; //$NON-NLS-1$
+
+ /** The singleton instance */
+ private static DdmsPlugin sPlugin;
+
+ /** Location of the adb command line executable */
+ private static String sAdbLocation;
+ private static String sToolsFolder;
+ private static String sHprofConverter;
+
+ private boolean mHasDebuggerConnectors;
+ /** debugger connectors for already running apps.
+ * Initialized from an extension point.
+ */
+ private IDebuggerConnector[] mDebuggerConnectors;
+ private ITraceviewLauncher[] mTraceviewLaunchers;
+ private List<IClientAction> mClientSpecificActions = null;
+
+ /** Console for DDMS log message */
+ private MessageConsole mDdmsConsole;
+
+ private IDevice mCurrentDevice;
+ private Client mCurrentClient;
+ private boolean mListeningToUiSelection = false;
+
+ private final ArrayList<ISelectionListener> mListeners = new ArrayList<ISelectionListener>();
+
+ private Color mRed;
+
+
+ /**
+ * Classes which implement this interface provide methods that deals
+ * with {@link IDevice} and {@link Client} selectionchanges.
+ */
+ public interface ISelectionListener {
+
+ /**
+ * Sent when a new {@link Client} is selected.
+ * @param selectedClient The selected client. If null, no clients are selected.
+ */
+ public void selectionChanged(Client selectedClient);
+
+ /**
+ * Sent when a new {@link IDevice} is selected.
+ * @param selectedDevice the selected device. If null, no devices are selected.
+ */
+ public void selectionChanged(IDevice selectedDevice);
+ }
+
+ /**
+ * The constructor
+ */
+ public DdmsPlugin() {
+ sPlugin = this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+
+ final Display display = getDisplay();
+
+ // get the eclipse store
+ final IPreferenceStore eclipseStore = getPreferenceStore();
+
+ AndroidDebugBridge.addDeviceChangeListener(this);
+
+ DdmUiPreferences.setStore(eclipseStore);
+
+ //DdmUiPreferences.displayCharts();
+
+ // set the consoles.
+ mDdmsConsole = new MessageConsole("DDMS", null); //$NON-NLS-1$
+ ConsolePlugin.getDefault().getConsoleManager().addConsoles(
+ new IConsole[] {
+ mDdmsConsole
+ });
+
+ final MessageConsoleStream consoleStream = mDdmsConsole.newMessageStream();
+ final MessageConsoleStream errorConsoleStream = mDdmsConsole.newMessageStream();
+ mRed = new Color(display, 0xFF, 0x00, 0x00);
+
+ // because this can be run, in some cases, by a non UI thread, and because
+ // changing the console properties update the UI, we need to make this change
+ // in the UI thread.
+ display.asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ errorConsoleStream.setColor(mRed);
+ }
+ });
+
+ // set up the ddms log to use the ddms console.
+ Log.setLogOutput(new ILogOutput() {
+ @Override
+ public void printLog(LogLevel logLevel, String tag, String message) {
+ if (logLevel.getPriority() >= LogLevel.ERROR.getPriority()) {
+ printToStream(errorConsoleStream, tag, message);
+ showConsoleView(mDdmsConsole);
+ } else {
+ printToStream(consoleStream, tag, message);
+ }
+ }
+
+ @Override
+ public void printAndPromptLog(final LogLevel logLevel, final String tag,
+ final String message) {
+ printLog(logLevel, tag, message);
+ // dialog box only run in UI thread..
+ display.asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ Shell shell = display.getActiveShell();
+ if (logLevel == LogLevel.ERROR) {
+ MessageDialog.openError(shell, tag, message);
+ } else {
+ MessageDialog.openWarning(shell, tag, message);
+ }
+ }
+ });
+ }
+
+ });
+
+ // set up the ddms console to use this objects
+ DdmConsole.setConsole(new IDdmConsole() {
+ @Override
+ public void printErrorToConsole(String message) {
+ printToStream(errorConsoleStream, null, message);
+ showConsoleView(mDdmsConsole);
+ }
+ @Override
+ public void printErrorToConsole(String[] messages) {
+ for (String m : messages) {
+ printToStream(errorConsoleStream, null, m);
+ }
+ showConsoleView(mDdmsConsole);
+ }
+ @Override
+ public void printToConsole(String message) {
+ printToStream(consoleStream, null, message);
+ }
+ @Override
+ public void printToConsole(String[] messages) {
+ for (String m : messages) {
+ printToStream(consoleStream, null, m);
+ }
+ }
+ });
+
+ // set the listener for the preference change
+ eclipseStore.addPropertyChangeListener(new IPropertyChangeListener() {
+ @Override
+ public void propertyChange(PropertyChangeEvent event) {
+ // get the name of the property that changed.
+ String property = event.getProperty();
+
+ if (PreferenceInitializer.ATTR_DEBUG_PORT_BASE.equals(property)) {
+ DdmPreferences.setDebugPortBase(
+ eclipseStore.getInt(PreferenceInitializer.ATTR_DEBUG_PORT_BASE));
+ } else if (PreferenceInitializer.ATTR_SELECTED_DEBUG_PORT.equals(property)) {
+ DdmPreferences.setSelectedDebugPort(
+ eclipseStore.getInt(PreferenceInitializer.ATTR_SELECTED_DEBUG_PORT));
+ } else if (PreferenceInitializer.ATTR_THREAD_INTERVAL.equals(property)) {
+ DdmUiPreferences.setThreadRefreshInterval(
+ eclipseStore.getInt(PreferenceInitializer.ATTR_THREAD_INTERVAL));
+ } else if (PreferenceInitializer.ATTR_LOG_LEVEL.equals(property)) {
+ DdmPreferences.setLogLevel(
+ eclipseStore.getString(PreferenceInitializer.ATTR_LOG_LEVEL));
+ } else if (PreferenceInitializer.ATTR_TIME_OUT.equals(property)) {
+ DdmPreferences.setTimeOut(
+ eclipseStore.getInt(PreferenceInitializer.ATTR_TIME_OUT));
+ } else if (PreferenceInitializer.ATTR_USE_ADBHOST.equals(property)) {
+ DdmPreferences.setUseAdbHost(
+ eclipseStore.getBoolean(PreferenceInitializer.ATTR_USE_ADBHOST));
+ } else if (PreferenceInitializer.ATTR_ADBHOST_VALUE.equals(property)) {
+ DdmPreferences.setAdbHostValue(
+ eclipseStore.getString(PreferenceInitializer.ATTR_ADBHOST_VALUE));
+ }
+ }
+ });
+
+ // do some last initializations
+
+ // set the preferences.
+ PreferenceInitializer.setupPreferences();
+
+ // this class is set as the main source revealer and will look at all the implementations
+ // of the extension point. see #reveal(String, String, int)
+ StackTracePanel.setSourceRevealer(this);
+
+ /*
+ * Load the extension point implementations.
+ * The first step is to load the IConfigurationElement representing the implementations.
+ * The 2nd step is to use these objects to instantiate the implementation classes.
+ *
+ * Because the 2nd step will trigger loading the plug-ins providing the implementations,
+ * and those plug-ins could access DDMS classes (like ADT), this 2nd step should be done
+ * in a Job to ensure that DDMS is loaded, so that the other plug-ins can load.
+ *
+ * Both steps could be done in the 2nd step but some of DDMS UI rely on knowing if there
+ * is an implementation or not (DeviceView), so we do the first steps in start() and, in
+ * some case, record it.
+ *
+ */
+
+ // get the IConfigurationElement for the debuggerConnector right away.
+ final IConfigurationElement[] dcce = findConfigElements(
+ "com.android.ide.eclipse.ddms.debuggerConnector"); //$NON-NLS-1$
+ mHasDebuggerConnectors = dcce.length > 0;
+
+ // get the other configElements and instantiante them in a Job.
+ new Job(Messages.DdmsPlugin_DDMS_Post_Create_Init) {
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ try {
+ // init the lib
+ AndroidDebugBridge.init(true /* debugger support */);
+
+ // get the available adb locators
+ IConfigurationElement[] elements = findConfigElements(
+ "com.android.ide.eclipse.ddms.toolsLocator"); //$NON-NLS-1$
+
+ IToolsLocator[] locators = instantiateToolsLocators(elements);
+
+ for (IToolsLocator locator : locators) {
+ try {
+ String adbLocation = locator.getAdbLocation();
+ String traceviewLocation = locator.getTraceViewLocation();
+ String hprofConvLocation = locator.getHprofConvLocation();
+ if (adbLocation != null && traceviewLocation != null &&
+ hprofConvLocation != null) {
+ // checks if the location is valid.
+ if (setToolsLocation(adbLocation, hprofConvLocation,
+ traceviewLocation)) {
+
+ AndroidDebugBridge.createBridge(sAdbLocation,
+ true /* forceNewBridge */);
+
+ // no need to look at the other locators.
+ break;
+ }
+ }
+ } catch (Throwable t) {
+ // ignore, we'll just not use this implementation.
+ }
+ }
+
+ // get the available debugger connectors
+ mDebuggerConnectors = instantiateDebuggerConnectors(dcce);
+
+ // get the available Traceview Launchers.
+ elements = findConfigElements("com.android.ide.eclipse.ddms.traceviewLauncher"); //$NON-NLS-1$
+ mTraceviewLaunchers = instantiateTraceviewLauncher(elements);
+
+ return Status.OK_STATUS;
+ } catch (CoreException e) {
+ return e.getStatus();
+ }
+ }
+ }.schedule();
+ }
+
+ private void showConsoleView(MessageConsole console) {
+ ConsolePlugin.getDefault().getConsoleManager().showConsoleView(console);
+ }
+
+
+ /** Obtain a list of configuration elements that extend the given extension point. */
+ IConfigurationElement[] findConfigElements(String extensionPointId) {
+ // get the adb location from an implementation of the ADB Locator extension point.
+ IExtensionRegistry extensionRegistry = Platform.getExtensionRegistry();
+ IExtensionPoint extensionPoint = extensionRegistry.getExtensionPoint(extensionPointId);
+ if (extensionPoint != null) {
+ return extensionPoint.getConfigurationElements();
+ }
+
+ // shouldn't happen or it means the plug-in is broken.
+ return new IConfigurationElement[0];
+ }
+
+ /**
+ * Finds if any other plug-in is extending the exposed Extension Point called adbLocator.
+ *
+ * @return an array of all locators found, or an empty array if none were found.
+ */
+ private IToolsLocator[] instantiateToolsLocators(IConfigurationElement[] configElements)
+ throws CoreException {
+ ArrayList<IToolsLocator> list = new ArrayList<IToolsLocator>();
+
+ if (configElements.length > 0) {
+ // only use the first one, ignore the others.
+ IConfigurationElement configElement = configElements[0];
+
+ // instantiate the class
+ Object obj = configElement.createExecutableExtension("class"); //$NON-NLS-1$
+ if (obj instanceof IToolsLocator) {
+ list.add((IToolsLocator) obj);
+ }
+ }
+
+ return list.toArray(new IToolsLocator[list.size()]);
+ }
+
+ /**
+ * Finds if any other plug-in is extending the exposed Extension Point called debuggerConnector.
+ *
+ * @return an array of all locators found, or an empty array if none were found.
+ */
+ private IDebuggerConnector[] instantiateDebuggerConnectors(
+ IConfigurationElement[] configElements) throws CoreException {
+ ArrayList<IDebuggerConnector> list = new ArrayList<IDebuggerConnector>();
+
+ if (configElements.length > 0) {
+ // only use the first one, ignore the others.
+ IConfigurationElement configElement = configElements[0];
+
+ // instantiate the class
+ Object obj = configElement.createExecutableExtension("class"); //$NON-NLS-1$
+ if (obj instanceof IDebuggerConnector) {
+ list.add((IDebuggerConnector) obj);
+ }
+ }
+
+ return list.toArray(new IDebuggerConnector[list.size()]);
+ }
+
+ /**
+ * Finds if any other plug-in is extending the exposed Extension Point called traceviewLauncher.
+ *
+ * @return an array of all locators found, or an empty array if none were found.
+ */
+ private ITraceviewLauncher[] instantiateTraceviewLauncher(
+ IConfigurationElement[] configElements)
+ throws CoreException {
+ ArrayList<ITraceviewLauncher> list = new ArrayList<ITraceviewLauncher>();
+
+ if (configElements.length > 0) {
+ // only use the first one, ignore the others.
+ IConfigurationElement configElement = configElements[0];
+
+ // instantiate the class
+ Object obj = configElement.createExecutableExtension("class"); //$NON-NLS-1$
+ if (obj instanceof ITraceviewLauncher) {
+ list.add((ITraceviewLauncher) obj);
+ }
+ }
+
+ return list.toArray(new ITraceviewLauncher[list.size()]);
+ }
+
+ /**
+ * Returns the classes that implement {@link IClientAction} in each of the extensions that
+ * extend clientAction extension point.
+ * @throws CoreException
+ */
+ private List<IClientAction> instantiateClientSpecificActions(IConfigurationElement[] elements)
+ throws CoreException {
+ if (elements == null || elements.length == 0) {
+ return Collections.emptyList();
+ }
+
+ List<IClientAction> extensions = new ArrayList<IClientAction>(1);
+
+ for (IConfigurationElement e : elements) {
+ Object o = e.createExecutableExtension("class"); //$NON-NLS-1$
+ if (o instanceof IClientAction) {
+ extensions.add((IClientAction) o);
+ }
+ }
+
+ return extensions;
+ }
+
+ public static Display getDisplay() {
+ IWorkbench bench = sPlugin.getWorkbench();
+ if (bench != null) {
+ return bench.getDisplay();
+ }
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ AndroidDebugBridge.removeDeviceChangeListener(this);
+
+ AndroidDebugBridge.terminate();
+
+ mRed.dispose();
+
+ sPlugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static DdmsPlugin getDefault() {
+ return sPlugin;
+ }
+
+ public static String getAdb() {
+ return sAdbLocation;
+ }
+
+ public static File getPlatformToolsFolder() {
+ return new File(sAdbLocation).getParentFile();
+ }
+
+ public static String getToolsFolder() {
+ return sToolsFolder;
+ }
+
+ public static String getHprofConverter() {
+ return sHprofConverter;
+ }
+
+ /**
+ * Stores the adb location. This returns true if the location is an existing file.
+ */
+ private static boolean setToolsLocation(String adbLocation, String hprofConvLocation,
+ String traceViewLocation) {
+
+ File adb = new File(adbLocation);
+ File hprofConverter = new File(hprofConvLocation);
+ File traceview = new File(traceViewLocation);
+
+ String missing = "";
+ if (adb.isFile() == false) {
+ missing += adb.getAbsolutePath() + " ";
+ }
+ if (hprofConverter.isFile() == false) {
+ missing += hprofConverter.getAbsolutePath() + " ";
+ }
+ if (traceview.isFile() == false) {
+ missing += traceview.getAbsolutePath() + " ";
+ }
+
+ if (missing.length() > 0) {
+ String msg = String.format("DDMS files not found: %1$s", missing);
+ Log.e("DDMS", msg);
+ Status status = new Status(IStatus.ERROR, PLUGIN_ID, msg, null /*exception*/);
+ getDefault().getLog().log(status);
+ return false;
+ }
+
+ sAdbLocation = adbLocation;
+ sHprofConverter = hprofConverter.getAbsolutePath();
+ DdmUiPreferences.setTraceviewLocation(traceview.getAbsolutePath());
+
+ sToolsFolder = traceview.getParent();
+
+ return true;
+ }
+
+ /**
+ * Set the location of the adb executable and optionally starts adb
+ * @param adb location of adb
+ * @param startAdb flag to start adb
+ */
+ public static void setToolsLocation(String adbLocation, boolean startAdb,
+ String hprofConvLocation, String traceViewLocation) {
+
+ if (setToolsLocation(adbLocation, hprofConvLocation, traceViewLocation)) {
+ // starts the server in a thread in case this is blocking.
+ if (startAdb) {
+ new Thread() {
+ @Override
+ public void run() {
+ // create and start the bridge
+ try {
+ AndroidDebugBridge.createBridge(sAdbLocation,
+ false /* forceNewBridge */);
+ } catch (Throwable t) {
+ Status status = new Status(IStatus.ERROR, PLUGIN_ID,
+ "Failed to create AndroidDebugBridge", t);
+ getDefault().getLog().log(status);
+ }
+ }
+ }.start();
+ }
+ }
+ }
+
+ /**
+ * Returns whether there are implementations of the debuggerConnectors extension point.
+ * <p/>
+ * This is guaranteed to return the correct value as soon as the plug-in is loaded.
+ */
+ public boolean hasDebuggerConnectors() {
+ return mHasDebuggerConnectors;
+ }
+
+ /**
+ * Returns the implementations of {@link IDebuggerConnector}.
+ * <p/>
+ * There may be a small amount of time right after the plug-in load where this can return
+ * null even if there are implementation.
+ * <p/>
+ * Since the use of the implementation likely require user input, the UI can use
+ * {@link #hasDebuggerConnectors()} to know if there are implementations before they are loaded.
+ */
+ public IDebuggerConnector[] getDebuggerConnectors() {
+ return mDebuggerConnectors;
+ }
+
+ public synchronized void addSelectionListener(ISelectionListener listener) {
+ mListeners.add(listener);
+
+ // notify the new listener of the current selection
+ listener.selectionChanged(mCurrentDevice);
+ listener.selectionChanged(mCurrentClient);
+ }
+
+ public synchronized void removeSelectionListener(ISelectionListener listener) {
+ mListeners.remove(listener);
+ }
+
+ public synchronized void setListeningState(boolean state) {
+ mListeningToUiSelection = state;
+ }
+
+ /**
+ * Sent when the a device is connected to the {@link AndroidDebugBridge}.
+ * <p/>
+ * This is sent from a non UI thread.
+ * @param device the new device.
+ *
+ * @see IDeviceChangeListener#deviceConnected(IDevice)
+ */
+ @Override
+ public void deviceConnected(IDevice device) {
+ // if we are listening to selection coming from the ui, then we do nothing, as
+ // any change in the devices/clients, will be handled by the UI, and we'll receive
+ // selection notification through our implementation of IUiSelectionListener.
+ if (mListeningToUiSelection == false) {
+ if (mCurrentDevice == null) {
+ handleDefaultSelection(device);
+ }
+ }
+ }
+
+ /**
+ * Sent when the a device is disconnected to the {@link AndroidDebugBridge}.
+ * <p/>
+ * This is sent from a non UI thread.
+ * @param device the new device.
+ *
+ * @see IDeviceChangeListener#deviceDisconnected(IDevice)
+ */
+ @Override
+ public void deviceDisconnected(IDevice device) {
+ // if we are listening to selection coming from the ui, then we do nothing, as
+ // any change in the devices/clients, will be handled by the UI, and we'll receive
+ // selection notification through our implementation of IUiSelectionListener.
+ if (mListeningToUiSelection == false) {
+ // test if the disconnected device was the default selection.
+ if (mCurrentDevice == device) {
+ // try to find a new device
+ AndroidDebugBridge bridge = AndroidDebugBridge.getBridge();
+ if (bridge != null) {
+ // get the device list
+ IDevice[] devices = bridge.getDevices();
+
+ // check if we still have devices
+ if (devices.length == 0) {
+ handleDefaultSelection((IDevice)null);
+ } else {
+ handleDefaultSelection(devices[0]);
+ }
+ } else {
+ handleDefaultSelection((IDevice)null);
+ }
+ }
+ }
+ }
+
+ /**
+ * Sent when a device data changed, or when clients are started/terminated on the device.
+ * <p/>
+ * This is sent from a non UI thread.
+ * @param device the device that was updated.
+ * @param changeMask the mask indicating what changed.
+ *
+ * @see IDeviceChangeListener#deviceChanged(IDevice)
+ */
+ @Override
+ public void deviceChanged(IDevice device, int changeMask) {
+ // if we are listening to selection coming from the ui, then we do nothing, as
+ // any change in the devices/clients, will be handled by the UI, and we'll receive
+ // selection notification through our implementation of IUiSelectionListener.
+ if (mListeningToUiSelection == false) {
+
+ // check if this is our device
+ if (device == mCurrentDevice) {
+ if (mCurrentClient == null) {
+ handleDefaultSelection(device);
+ } else {
+ // get the clients and make sure ours is still in there.
+ Client[] clients = device.getClients();
+ boolean foundClient = false;
+ for (Client client : clients) {
+ if (client == mCurrentClient) {
+ foundClient = true;
+ break;
+ }
+ }
+
+ // if we haven't found our client, lets look for a new one
+ if (foundClient == false) {
+ mCurrentClient = null;
+ handleDefaultSelection(device);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Sent when a new {@link IDevice} and {@link Client} are selected.
+ * @param selectedDevice the selected device. If null, no devices are selected.
+ * @param selectedClient The selected client. If null, no clients are selected.
+ */
+ @Override
+ public synchronized void selectionChanged(IDevice selectedDevice, Client selectedClient) {
+ if (mCurrentDevice != selectedDevice) {
+ mCurrentDevice = selectedDevice;
+
+ // notify of the new default device
+ for (ISelectionListener listener : mListeners) {
+ listener.selectionChanged(mCurrentDevice);
+ }
+ }
+
+ if (mCurrentClient != selectedClient) {
+ mCurrentClient = selectedClient;
+
+ // notify of the new default client
+ for (ISelectionListener listener : mListeners) {
+ listener.selectionChanged(mCurrentClient);
+ }
+ }
+ }
+
+ /**
+ * Handles a default selection of a {@link IDevice} and {@link Client}.
+ * @param device the selected device
+ */
+ private void handleDefaultSelection(final IDevice device) {
+ // because the listener expect to receive this from the UI thread, and this is called
+ // from the AndroidDebugBridge notifications, we need to run this in the UI thread.
+ try {
+ Display display = getDisplay();
+
+ display.asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ // set the new device if different.
+ boolean newDevice = false;
+ if (mCurrentDevice != device) {
+ mCurrentDevice = device;
+ newDevice = true;
+
+ // notify of the new default device
+ for (ISelectionListener listener : mListeners) {
+ listener.selectionChanged(mCurrentDevice);
+ }
+ }
+
+ if (device != null) {
+ // if this is a device switch or the same device but we didn't find a valid
+ // client the last time, we go look for a client to use again.
+ if (newDevice || mCurrentClient == null) {
+ // now get the new client
+ Client[] clients = device.getClients();
+ if (clients.length > 0) {
+ handleDefaultSelection(clients[0]);
+ } else {
+ handleDefaultSelection((Client)null);
+ }
+ }
+ } else {
+ handleDefaultSelection((Client)null);
+ }
+ }
+ });
+ } catch (SWTException e) {
+ // display is disposed. Do nothing since we're quitting anyway.
+ }
+ }
+
+ private void handleDefaultSelection(Client client) {
+ mCurrentClient = client;
+
+ // notify of the new default client
+ for (ISelectionListener listener : mListeners) {
+ listener.selectionChanged(mCurrentClient);
+ }
+ }
+
+ /**
+ * Prints a message, associated with a project to the specified stream
+ * @param stream The stream to write to
+ * @param tag The tag associated to the message. Can be null
+ * @param message The message to print.
+ */
+ private static synchronized void printToStream(MessageConsoleStream stream, String tag,
+ String message) {
+ String dateTag = getMessageTag(tag);
+
+ stream.print(dateTag);
+ if (!dateTag.endsWith(" ")) {
+ stream.print(" "); //$NON-NLS-1$
+ }
+ stream.println(message);
+ }
+
+ /**
+ * Creates a string containing the current date/time, and the tag
+ * @param tag The tag associated to the message. Can be null
+ * @return The dateTag
+ */
+ private static String getMessageTag(String tag) {
+ Calendar c = Calendar.getInstance();
+
+ if (tag == null) {
+ return String.format(Messages.DdmsPlugin_Message_Tag_Mask_1, c);
+ }
+
+ return String.format(Messages.DdmsPlugin_Message_Tag_Mask_2, c, tag);
+ }
+
+ /**
+ * Implementation of com.android.ddmuilib.StackTracePanel.ISourceRevealer.
+ */
+ @Override
+ public void reveal(String applicationName, String className, int line) {
+ JavaSourceRevealer.reveal(applicationName, className, line);
+ }
+
+ public boolean launchTraceview(String osPath) {
+ if (mTraceviewLaunchers != null) {
+ for (ITraceviewLauncher launcher : mTraceviewLaunchers) {
+ try {
+ if (launcher.openFile(osPath)) {
+ return true;
+ }
+ } catch (Throwable t) {
+ // ignore, we'll just not use this implementation.
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the list of clients that extend the clientAction extension point.
+ */
+ @NonNull
+ public synchronized List<IClientAction> getClientSpecificActions() {
+ if (mClientSpecificActions == null) {
+ // get available client specific action extensions
+ IConfigurationElement[] elements =
+ findConfigElements("com.android.ide.eclipse.ddms.clientAction"); //$NON-NLS-1$
+ try {
+ mClientSpecificActions = instantiateClientSpecificActions(elements);
+ } catch (CoreException e) {
+ mClientSpecificActions = Collections.emptyList();
+ }
+ }
+
+ return mClientSpecificActions;
+ }
+
+ private LogCatMonitor mLogCatMonitor;
+ public void startLogCatMonitor(IDevice device) {
+ if (mLogCatMonitor == null) {
+ mLogCatMonitor = new LogCatMonitor(getDebuggerConnectors(), getPreferenceStore());
+ }
+
+ mLogCatMonitor.monitorDevice(device);
+ }
+
+ /** Returns an image descriptor for the image file at the given plug-in relative path */
+ public static ImageDescriptor getImageDescriptor(String path) {
+ return imageDescriptorFromPlugin(PLUGIN_ID, path);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/IClientAction.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/IClientAction.java
new file mode 100644
index 000000000..c23197599
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/IClientAction.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2012 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.ide.eclipse.ddms;
+
+import com.android.ddmlib.Client;
+
+import org.eclipse.jface.action.Action;
+
+public interface IClientAction {
+ Action getAction();
+ void selectedClientChanged(Client c);
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/IDebuggerConnector.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/IDebuggerConnector.java
new file mode 100644
index 000000000..c22dfd231
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/IDebuggerConnector.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2010 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.ide.eclipse.ddms;
+
+/**
+ * Classes which implement this interface provides a way to connect a debugger to a VM running
+ * on a connected device.
+ */
+public interface IDebuggerConnector {
+ /**
+ * Is this application from a project present in the workspace?
+ * @param appName name of the application. This is typically the application's package, but
+ * can be different if the component was setup to run in its own process.
+ * @return true if there is a project in the workspace containing the given app.
+ */
+ boolean isWorkspaceApp(String appName);
+
+ /**
+ * Connects a debugger to a VM identified by its appName.
+ * <p/>
+ * The given port is tied to the application and should be used if possible. However the
+ * "selected" port can also be used if needed.
+ * @param appName the name of the application. Usually the application's package but this
+ * can be different if the component was setup to run in it's own process.
+ * @param appPort the preferred connection port.
+ * @param selectedPort the port value for the selected application
+ * @return true if success.
+ */
+ boolean connectDebugger(String appName, int appPort, int selectedPort);
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/ISourceRevealer.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/ISourceRevealer.java
new file mode 100644
index 000000000..078203f52
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/ISourceRevealer.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2010 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.ide.eclipse.ddms;
+
+/**
+ * Classes which implement this interface are able to open a source file based on the provided
+ * constraints.
+ */
+public interface ISourceRevealer {
+ /**
+ * Reveal a particular line in the given application.
+ * @param applicationName the name of the application running the source.
+ * @param className the fully qualified class name
+ * @param line the line to reveal
+ * @return true if the source was revealed.
+ */
+ boolean reveal(String applicationName, String className, int line);
+
+ /**
+ * Reveal a particular Java method.
+ * @param fqmn fully qualified method name
+ * @param fileName file name that contains the method, null if not known
+ * @param lineNumber line number in the file, -1 if not known
+ * @param perspective If not null, switch to this perspective before
+ * revealing the source
+ * @return true if the source was revealed.
+ */
+ boolean revealMethod(String fqmn, String fileName, int lineNumber, String perspective);
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/IToolsLocator.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/IToolsLocator.java
new file mode 100644
index 000000000..5b53db3fb
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/IToolsLocator.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2010 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.ide.eclipse.ddms;
+
+/**
+ * Classes which implement this interface provides the location of various SDK tools.
+ */
+public interface IToolsLocator {
+
+ /**
+ * Queries the location of ADB
+ * @return A full OS path to the location of adb.
+ */
+ String getAdbLocation();
+
+ /**
+ * Queries the location of Traceview
+ * @return A full OS path to the location of traceview
+ */
+ String getTraceViewLocation();
+
+ /**
+ * Queries the location of hprof-conv
+ * @return A full OS path to the location of hprof-conv.
+ */
+ String getHprofConvLocation();
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/ITraceviewLauncher.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/ITraceviewLauncher.java
new file mode 100644
index 000000000..7542b8838
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/ITraceviewLauncher.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2011 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.ide.eclipse.ddms;
+
+/**
+ * Classes which implement this interface provides a way to open a traceview file.
+ */
+public interface ITraceviewLauncher {
+
+ boolean openFile(String osPath);
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/JavaSourceRevealer.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/JavaSourceRevealer.java
new file mode 100644
index 000000000..6f9086a52
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/JavaSourceRevealer.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2011 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.ide.eclipse.ddms;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class JavaSourceRevealer {
+ private static final String SOURCE_REVEALER_EXTENSION_ID =
+ "com.android.ide.eclipse.ddms.sourceRevealer"; //$NON-NLS-1$
+
+ private static List<ISourceRevealer> sSourceRevealers = instantiateSourceRevealers();
+
+ /** Instantiate all providers of the {@link #SOURCE_REVEALER_EXTENSION_ID} extension. */
+ private static List<ISourceRevealer> instantiateSourceRevealers() {
+ IConfigurationElement[] configElements =
+ DdmsPlugin.getDefault().findConfigElements(SOURCE_REVEALER_EXTENSION_ID);
+
+ List<ISourceRevealer> providers = new ArrayList<ISourceRevealer>();
+
+ for (IConfigurationElement configElement : configElements) {
+ // instantiate the class
+ Object obj = null;
+ try {
+ obj = configElement.createExecutableExtension("class"); //$NON-NLS-1$
+ } catch (CoreException e) {
+ // ignore exception while instantiating this class.
+ }
+
+ if (obj instanceof ISourceRevealer) {
+ providers.add((ISourceRevealer) obj);
+ }
+ }
+
+ return providers;
+ }
+
+ public static boolean reveal(String applicationName, String className, int line) {
+ for (ISourceRevealer revealer : sSourceRevealers) {
+ try {
+ if (revealer.reveal(applicationName, className, line)) {
+ return true;
+ }
+ } catch (Throwable t) {
+ // ignore, we'll just not use this implementation.
+ }
+ }
+
+ return false;
+ }
+
+ public static boolean revealMethod(String fqmn, String fileName, int linenumber,
+ String perspective) {
+ for (ISourceRevealer revealer : sSourceRevealers) {
+ try {
+ if (revealer.revealMethod(fqmn, fileName, linenumber, perspective)) {
+ return true;
+ }
+ } catch (Throwable t) {
+ // ignore, we'll just not use this implementation.
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/LogCatMonitor.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/LogCatMonitor.java
new file mode 100644
index 000000000..e99a637be
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/LogCatMonitor.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2011 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.ide.eclipse.ddms;
+
+import com.android.ddmlib.AndroidDebugBridge;
+import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener;
+import com.android.ddmlib.IDevice;
+import com.android.ddmlib.Log.LogLevel;
+import com.android.ddmlib.logcat.LogCatMessage;
+import com.android.ddmuilib.logcat.ILogCatBufferChangeListener;
+import com.android.ddmuilib.logcat.LogCatReceiver;
+import com.android.ddmuilib.logcat.LogCatReceiverFactory;
+import com.android.ide.eclipse.ddms.views.LogCatView;
+
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * LogCatMonitor helps in monitoring the logcat output from a set of devices.
+ * It scans through the received logcat messages, and activates the logcat view
+ * if any message is deemed important.
+ */
+public class LogCatMonitor {
+ public static final String AUTO_MONITOR_PREFKEY = "ddms.logcat.automonitor"; //$NON-NLS-1$
+ public static final String AUTO_MONITOR_LOGLEVEL = "ddms.logcat.auotmonitor.level"; //$NON-NLS-1$
+ private static final String AUTO_MONITOR_PROMPT_SHOWN = "ddms.logcat.automonitor.userprompt"; //$NON-NLS-1$
+
+ private IPreferenceStore mPrefStore;
+ private Map<String, DeviceData> mMonitoredDevices;
+ private IDebuggerConnector[] mConnectors;
+
+ private int mMinMessagePriority;
+
+ /**
+ * Flag that controls when the logcat stream is checked. This flag is set when the user
+ * performs a launch, and is reset as soon as the logcat view is displayed.
+ */
+ final AtomicBoolean mMonitorEnabled = new AtomicBoolean(false);
+
+ public LogCatMonitor(IDebuggerConnector[] debuggerConnectors, IPreferenceStore prefStore) {
+ mConnectors = debuggerConnectors;
+ mPrefStore = prefStore;
+ mMinMessagePriority =
+ LogLevel.getByString(mPrefStore.getString(AUTO_MONITOR_LOGLEVEL)).getPriority();
+
+ mMonitoredDevices = new HashMap<String, DeviceData>();
+
+ AndroidDebugBridge.addDeviceChangeListener(new IDeviceChangeListener() {
+ @Override
+ public void deviceDisconnected(IDevice device) {
+ unmonitorDevice(device.getSerialNumber());
+ mMonitoredDevices.remove(device.getSerialNumber());
+ }
+
+ @Override
+ public void deviceConnected(IDevice device) {
+ }
+
+ @Override
+ public void deviceChanged(IDevice device, int changeMask) {
+ }
+ });
+
+ mPrefStore.addPropertyChangeListener(new IPropertyChangeListener() {
+ @Override
+ public void propertyChange(PropertyChangeEvent event) {
+ if (AUTO_MONITOR_PREFKEY.equals(event.getProperty())
+ && event.getNewValue().equals(false)) {
+ unmonitorAllDevices();
+ } else if (AUTO_MONITOR_LOGLEVEL.equals(event.getProperty())) {
+ mMinMessagePriority =
+ LogLevel.getByString((String) event.getNewValue()).getPriority();
+ }
+ }
+ });
+ }
+
+ private void unmonitorAllDevices() {
+ for (String device : mMonitoredDevices.keySet()) {
+ unmonitorDevice(device);
+ }
+
+ mMonitoredDevices.clear();
+ }
+
+ private void unmonitorDevice(String deviceSerial) {
+ DeviceData data = mMonitoredDevices.get(deviceSerial);
+ if (data == null) {
+ return;
+ }
+
+ data.receiver.removeMessageReceivedEventListener(data.bufferChangeListener);
+ }
+
+ public void monitorDevice(final IDevice device) {
+ if (!mPrefStore.getBoolean(AUTO_MONITOR_PREFKEY)) {
+ // do not monitor device if auto monitoring is off
+ return;
+ }
+
+ mMonitorEnabled.set(true);
+
+ if (mMonitoredDevices.keySet().contains(device.getSerialNumber())) {
+ // the device is already monitored
+ return;
+ }
+
+ LogCatReceiver r = LogCatReceiverFactory.INSTANCE.newReceiver(device, mPrefStore);
+ ILogCatBufferChangeListener l = new ILogCatBufferChangeListener() {
+ @Override
+ public void bufferChanged(List<LogCatMessage> addedMessages,
+ List<LogCatMessage> deletedMessages) {
+ checkMessages(addedMessages, device);
+ }
+ };
+ r.addMessageReceivedEventListener(l);
+
+ mMonitoredDevices.put(device.getSerialNumber(), new DeviceData(r, l));
+ }
+
+ private void checkMessages(List<LogCatMessage> receivedMessages, IDevice device) {
+ if (!mMonitorEnabled.get()) {
+ return;
+ }
+
+ // check the received list of messages to see if any of them are
+ // significant enough to be seen by the user. If so, activate the logcat view
+ // to display those messages
+ for (LogCatMessage m : receivedMessages) {
+ if (isImportantMessage(m)) {
+ focusLogCatView(device, m.getAppName());
+
+ // now that logcat view is active, no need to check messages until the next
+ // time user launches an application.
+ mMonitorEnabled.set(false);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Check whether a message is "important". Currently, we assume that a message is important if
+ * it is of severity level error or higher, and it belongs to an app currently in the workspace.
+ */
+ private boolean isImportantMessage(LogCatMessage m) {
+ if (m.getLogLevel().getPriority() < mMinMessagePriority) {
+ return false;
+ }
+
+ String app = m.getAppName();
+ for (IDebuggerConnector c : mConnectors) {
+ if (c.isWorkspaceApp(app)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private void focusLogCatView(final IDevice device, final String appName) {
+ Display.getDefault().asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ if (window == null) {
+ return;
+ }
+
+ IWorkbenchPage page = window.getActivePage();
+ if (page == null) {
+ return;
+ }
+
+ // if the logcat view is not visible, then prompt the user once to set
+ // logcat monitoring preferences
+ if (!isLogCatViewVisible(page)) {
+ boolean showLogCatView = promptUserOnce(page.getWorkbenchWindow().getShell());
+ if (!showLogCatView) {
+ return;
+ }
+ }
+
+ // display view
+ final LogCatView v = displayLogCatView(page);
+ if (v == null) {
+ return;
+ }
+
+ // select correct device
+ v.selectionChanged(device);
+
+ // select appropriate filter
+ v.selectTransientAppFilter(appName);
+ }
+
+ private boolean isLogCatViewVisible(IWorkbenchPage page) {
+ IViewPart view = page.findView(LogCatView.ID);
+ return view != null && page.isPartVisible(view);
+ }
+
+ private LogCatView displayLogCatView(IWorkbenchPage page) {
+ // if the view is already in the page, just bring it to the front
+ // without giving it focus.
+ IViewPart view = page.findView(LogCatView.ID);
+ if (view != null) {
+ page.bringToTop(view);
+ if (view instanceof LogCatView) {
+ return (LogCatView)view;
+ }
+ }
+
+ // if the view is not in the page, then create and show it.
+ try {
+ return (LogCatView) page.showView(LogCatView.ID);
+ } catch (PartInitException e) {
+ return null;
+ }
+ }
+
+ private boolean promptUserOnce(Shell shell) {
+ // see if this prompt was already displayed
+ boolean promptShown = mPrefStore.getBoolean(AUTO_MONITOR_PROMPT_SHOWN);
+ if (promptShown) {
+ return mPrefStore.getBoolean(AUTO_MONITOR_PREFKEY);
+ }
+
+ LogCatMonitorDialog dlg = new LogCatMonitorDialog(shell);
+ int r = dlg.open();
+
+ // save preference indicating that this dialog has been displayed once
+ mPrefStore.setValue(AUTO_MONITOR_PROMPT_SHOWN, true);
+ mPrefStore.setValue(AUTO_MONITOR_PREFKEY, dlg.shouldMonitor());
+ mPrefStore.setValue(AUTO_MONITOR_LOGLEVEL, dlg.getMinimumPriority());
+
+ return r == Window.OK && dlg.shouldMonitor();
+ }
+
+ });
+ }
+
+ private static class DeviceData {
+ public final LogCatReceiver receiver;
+ public final ILogCatBufferChangeListener bufferChangeListener;
+
+ public DeviceData(LogCatReceiver r, ILogCatBufferChangeListener l) {
+ receiver = r;
+ bufferChangeListener = l;
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/LogCatMonitorDialog.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/LogCatMonitorDialog.java
new file mode 100644
index 000000000..6194a0d9c
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/LogCatMonitorDialog.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2012 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.ide.eclipse.ddms;
+
+import com.android.ddmlib.Log.LogLevel;
+
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+
+public class LogCatMonitorDialog extends TitleAreaDialog {
+ private static final String TITLE = "Auto Monitor Logcat";
+ private static final String DEFAULT_MESSAGE =
+ "Would you like ADT to automatically monitor logcat \n" +
+ "output for messages from applications in the workspace?";
+
+ private boolean mShouldMonitor = true;
+
+ private static final String[] LOG_PRIORITIES = new String[] {
+ LogLevel.VERBOSE.getStringValue(),
+ LogLevel.DEBUG.getStringValue(),
+ LogLevel.INFO.getStringValue(),
+ LogLevel.WARN.getStringValue(),
+ LogLevel.ERROR.getStringValue(),
+ LogLevel.ASSERT.getStringValue(),
+ };
+ private static final int ERROR_PRIORITY_INDEX = 4;
+
+ private String mMinimumLogPriority = LOG_PRIORITIES[ERROR_PRIORITY_INDEX];
+
+ public LogCatMonitorDialog(Shell parentShell) {
+ super(parentShell);
+ setHelpAvailable(false);
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ setTitle(TITLE);
+ setMessage(DEFAULT_MESSAGE);
+
+ parent = (Composite) super.createDialogArea(parent);
+ Composite c = new Composite(parent, SWT.BORDER);
+ c.setLayout(new GridLayout(2, false));
+ GridData gd_c = new GridData(GridData.FILL_BOTH);
+ gd_c.grabExcessVerticalSpace = false;
+ gd_c.grabExcessHorizontalSpace = false;
+ c.setLayoutData(gd_c);
+
+ final Button disableButton = new Button(c, SWT.RADIO);
+ disableButton.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
+ disableButton.setText("No, do not monitor logcat output.");
+
+ final Button enableButton = new Button(c, SWT.RADIO);
+ enableButton.setText("Yes, monitor logcat and display logcat view if there are\n" +
+ "messages with priority higher than:");
+ enableButton.setSelection(true);
+
+ final Combo levelCombo = new Combo(c, SWT.READ_ONLY | SWT.DROP_DOWN);
+ levelCombo.setItems(LOG_PRIORITIES);
+ levelCombo.select(ERROR_PRIORITY_INDEX);
+
+ SelectionListener s = new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ if (e.getSource() == enableButton) {
+ mShouldMonitor = enableButton.getSelection();
+ levelCombo.setEnabled(mShouldMonitor);
+ } else if (e.getSource() == levelCombo) {
+ mMinimumLogPriority = LOG_PRIORITIES[levelCombo.getSelectionIndex()];
+ }
+ }
+ };
+
+ levelCombo.addSelectionListener(s);
+ enableButton.addSelectionListener(s);
+
+ return parent;
+ }
+
+ @Override
+ protected void createButtonsForButtonBar(Composite parent) {
+ // Only need OK button
+ createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL,
+ true);
+ }
+
+ public boolean shouldMonitor() {
+ return mShouldMonitor;
+ }
+
+ public String getMinimumPriority() {
+ return mMinimumLogPriority;
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/Perspective.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/Perspective.java
new file mode 100644
index 000000000..c98e9ca7f
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/Perspective.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2007 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.ide.eclipse.ddms;
+
+import com.android.ide.eclipse.ddms.views.AllocTrackerView;
+import com.android.ide.eclipse.ddms.views.DeviceView;
+import com.android.ide.eclipse.ddms.views.EmulatorControlView;
+import com.android.ide.eclipse.ddms.views.FileExplorerView;
+import com.android.ide.eclipse.ddms.views.HeapView;
+import com.android.ide.eclipse.ddms.views.LogCatView;
+import com.android.ide.eclipse.ddms.views.NetworkStatisticsView;
+import com.android.ide.eclipse.ddms.views.SysInfoView;
+import com.android.ide.eclipse.ddms.views.ThreadView;
+
+import org.eclipse.ui.IFolderLayout;
+import org.eclipse.ui.IPageLayout;
+import org.eclipse.ui.IPerspectiveFactory;
+
+public class Perspective implements IPerspectiveFactory {
+
+ public static String ID = "com.android.ide.eclipse.ddms.Perspective"; //$NON-NLS-1$
+
+ @Override
+ public void createInitialLayout(IPageLayout layout) {
+ // create a default layout that looks like the stand alone DDMS.
+
+ // no editor window
+ layout.setEditorAreaVisible(false);
+
+ String editorArea = layout.getEditorArea();
+ IFolderLayout folder;
+
+ folder = layout.createFolder("logcat", IPageLayout.BOTTOM, 0.8f, //$NON-NLS-1$
+ editorArea);
+ folder.addPlaceholder(LogCatView.ID + ":*"); //$NON-NLS-1$
+ folder.addView(LogCatView.ID);
+
+ folder = layout.createFolder("devices", IPageLayout.LEFT, 0.3f, //$NON-NLS-1$
+ editorArea);
+ folder.addPlaceholder(DeviceView.ID + ":*"); //$NON-NLS-1$
+ folder.addView(DeviceView.ID);
+
+ folder = layout.createFolder("ddms-detail", IPageLayout.RIGHT, 0.5f, //$NON-NLS-1$
+ editorArea);
+ folder.addPlaceholder(ThreadView.ID + ":*"); //$NON-NLS-1$
+ folder.addView(ThreadView.ID);
+ folder.addView(HeapView.ID);
+ folder.addView(AllocTrackerView.ID);
+ folder.addView(NetworkStatisticsView.ID);
+ folder.addView(FileExplorerView.ID);
+ folder.addView(EmulatorControlView.ID);
+ folder.addView(SysInfoView.ID);
+
+ layout.addPerspectiveShortcut("org.eclipse.ui.resourcePerspective"); //$NON-NLS-1$
+ layout.addPerspectiveShortcut("org.eclipse.debug.ui.DebugPerspective"); //$NON-NLS-1$
+ layout.addPerspectiveShortcut("org.eclipse.jdt.ui.JavaPerspective"); //$NON-NLS-1$
+
+ layout.addShowViewShortcut(DeviceView.ID);
+ layout.addShowViewShortcut(FileExplorerView.ID);
+ layout.addShowViewShortcut(HeapView.ID);
+ layout.addShowViewShortcut(AllocTrackerView.ID);
+ layout.addShowViewShortcut(LogCatView.ID);
+ layout.addShowViewShortcut(ThreadView.ID);
+ layout.addShowViewShortcut(NetworkStatisticsView.ID);
+ layout.addShowViewShortcut(SysInfoView.ID);
+
+ layout.addShowViewShortcut(IPageLayout.ID_RES_NAV);
+ layout.addShowViewShortcut(IPageLayout.ID_BOOKMARKS);
+ layout.addShowViewShortcut(IPageLayout.ID_OUTLINE);
+ layout.addShowViewShortcut(IPageLayout.ID_PROP_SHEET);
+ layout.addShowViewShortcut(IPageLayout.ID_PROBLEM_VIEW);
+ layout.addShowViewShortcut(IPageLayout.ID_PROGRESS_VIEW);
+ layout.addShowViewShortcut(IPageLayout.ID_TASK_LIST);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/editors/UiAutomatorViewer.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/editors/UiAutomatorViewer.java
new file mode 100644
index 000000000..0843018aa
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/editors/UiAutomatorViewer.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2012 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.ide.eclipse.ddms.editors;
+
+import com.android.ide.eclipse.base.InstallDetails;
+import com.android.uiautomator.UiAutomatorHelper.UiAutomatorResult;
+import com.android.uiautomator.UiAutomatorModel;
+import com.android.uiautomator.UiAutomatorView;
+
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.filesystem.IFileStore;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorSite;
+import org.eclipse.ui.IURIEditorInput;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.WorkbenchException;
+import org.eclipse.ui.ide.IDE;
+import org.eclipse.ui.part.EditorPart;
+
+import java.io.File;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class UiAutomatorViewer extends EditorPart {
+ private String mFilePath;
+ private UiAutomatorView mView;
+
+ @Override
+ public void doSave(IProgressMonitor arg0) {
+ }
+
+ @Override
+ public void doSaveAs() {
+ }
+
+ @Override
+ public boolean isSaveAsAllowed() {
+ return false;
+ }
+
+ @Override
+ public boolean isDirty() {
+ return false;
+ }
+
+ @Override
+ public void init(IEditorSite site, IEditorInput input) throws PartInitException {
+ // we use a IURIEditorInput to allow opening files not within the workspace
+ if (!(input instanceof IURIEditorInput)) {
+ throw new PartInitException("UI Automator Hierarchy View: unsupported input type.");
+ }
+
+ setSite(site);
+ setInput(input);
+ mFilePath = ((IURIEditorInput) input).getURI().getPath();
+
+ // set the editor part name to be the name of the file.
+ File f = new File(mFilePath);
+ setPartName(f.getName());
+ }
+
+ @Override
+ public void createPartControl(Composite parent) {
+ Composite c = new Composite(parent, SWT.NONE);
+ c.setLayout(new GridLayout(1, false));
+ GridData gd = new GridData(GridData.FILL_BOTH);
+ c.setLayoutData(gd);
+
+ mView = new UiAutomatorView(c, SWT.BORDER);
+ mView.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ if (mFilePath == null) {
+ return;
+ }
+
+ UiAutomatorModel model = null;
+ File modelFile = new File(mFilePath);
+ try {
+ model = new UiAutomatorModel(modelFile);
+ } catch (Exception e) {
+ MessageDialog.openError(parent.getShell(), "Error opening " + mFilePath,
+ "Unexpected error while parsing input: " + e.getMessage());
+ return;
+ }
+
+ mView.setModel(model, modelFile, null);
+ }
+
+ @Override
+ public void setFocus() {
+ }
+
+ public static boolean openEditor(final UiAutomatorResult r) {
+ final IFileStore fileStore = EFS.getLocalFileSystem().getStore(
+ new Path(r.uiHierarchy.getAbsolutePath()));
+ if (!fileStore.fetchInfo().exists()) {
+ return false;
+ }
+
+ final AtomicBoolean status = new AtomicBoolean(false);
+
+ final IWorkbench workbench = PlatformUI.getWorkbench();
+ workbench.getDisplay().syncExec(new Runnable() {
+ @Override
+ public void run() {
+ IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
+ if (window == null) {
+ return;
+ }
+
+ IWorkbenchPage page = window.getActivePage();
+ if (page == null) {
+ return;
+ }
+
+ // try to switch perspectives if possible
+ if (page.isEditorAreaVisible() == false && InstallDetails.isAdtInstalled()) {
+ try {
+ workbench.showPerspective("org.eclipse.jdt.ui.JavaPerspective", window); //$NON-NLS-1$
+ } catch (WorkbenchException e) {
+ }
+ }
+
+ IEditorPart editor = null;
+ try {
+ editor = IDE.openEditorOnFileStore(page, fileStore);
+ } catch (PartInitException e) {
+ return;
+ }
+
+ if (!(editor instanceof UiAutomatorViewer)) {
+ return;
+ }
+
+ ((UiAutomatorViewer) editor).setModel(r.model, r.uiHierarchy, r.screenshot);
+ status.set(true);
+ }
+ });
+
+ return status.get();
+ }
+
+ protected void setModel(UiAutomatorModel model, File modelFile, Image screenshot) {
+ mView.setModel(model, modelFile, screenshot);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/i18n/Messages.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/i18n/Messages.java
new file mode 100644
index 000000000..576b59873
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/i18n/Messages.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2011 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.ide.eclipse.ddms.i18n;
+
+import org.eclipse.osgi.util.NLS;
+
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME = "com.android.ide.eclipse.ddms.i18n.messages"; //$NON-NLS-1$
+ public static String DdmsPlugin_Message_Tag_Mask_1;
+ public static String DdmsPlugin_Message_Tag_Mask_2;
+ public static String DdmsPlugin_DDMS_Post_Create_Init;
+ public static String DeviceView_ADB_Error;
+ public static String DeviceView_ADB_Failed_Restart;
+ public static String DeviceView_Cause_GC;
+ public static String DeviceView_Cause_GC_Tooltip;
+ public static String DeviceView_Debug_Process;
+ public static String DeviceView_Debug_Process_Title;
+ public static String DeviceView_Debug_Process_Tooltip;
+ public static String DeviceView_Debug_Session_Failed;
+ public static String DeviceView_Dump_HPROF_File;
+ public static String DeviceView_Dump_HPROF_File_Not_Supported_By_VM;
+ public static String DeviceView_Dump_HPROF_File_Tooltip;
+ public static String DeviceView_Failed_To_Save_HPROF_Data;
+ public static String DeviceView_HPROF_Error;
+ public static String DeviceView_Process_Already_Being_Debugged;
+ public static String DeviceView_Process_Debug_Already_In_Use;
+ public static String DeviceView_Pulling_From_Device;
+ public static String DeviceView_Reset_ADB;
+ public static String DeviceView_Reset_ADB_Host_Deamon;
+ public static String DeviceView_Save_HPROF_File;
+ public static String DeviceView_Screen_Capture;
+ public static String DeviceView_Screen_Capture_Tooltip;
+ public static String DeviceView_Start_Method_Profiling;
+ public static String DeviceView_Start_Method_Profiling_Not_Suported_By_Vm;
+ public static String DeviceView_Start_Method_Profiling_Tooltip;
+ public static String DeviceView_Stop_Method_Profiling;
+ public static String DeviceView_Stop_Method_Profiling_Tooltip;
+ public static String DeviceView_Stop_Process;
+ public static String DeviceView_Stop_Process_Tooltip;
+ public static String DeviceView_Threads;
+ public static String DeviceView_Threads_Tooltip;
+ public static String DeviceView_Unable_Create_HPROF_For_Application;
+ public static String DeviceView_Unable_Download_HPROF_From_Device_One_Param_First_Message;
+ public static String DeviceView_Unable_Download_HPROF_From_Device_One_Param_Second_Message;
+ public static String DeviceView_Unable_Download_HPROF_From_Device_Two_Param;
+ public static String DeviceView_Update_Heap;
+ public static String DeviceView_Update_Heap_Tooltip;
+ public static String EventLogView_Clear_Log;
+ public static String EventLogView_Clears_Event_Log;
+ public static String EventLogView_Import_Bug_Report_Log;
+ public static String EventLogView_Imports_Bug_Report;
+ public static String EventLogView_Load_Log;
+ public static String EventLogView_Loads_Event_Log;
+ public static String EventLogView_Opens_Options_Panel;
+ public static String EventLogView_Options;
+ public static String EventLogView_Save_Log;
+ public static String EventLogView_Saves_Event_Log;
+ public static String FileExplorerView_Delete;
+ public static String FileExplorerView_Delete_The_Selection;
+ public static String FileExplorerView_Pull_File;
+ public static String FileExplorerView_Pull_File_From_File;
+ public static String FileExplorerView_Push_File;
+ public static String FileExplorerView_Push_File_Onto_Device;
+ public static String LogCatPreferencePage_Display_Font;
+ public static String LogCatPreferencePage_MaxMessages;
+ public static String LogCatPreferencePage_Switch_Perspective;
+ public static String LogCatPreferencePage_Switch_To;
+ public static String LogCatPreferencePage_AutoMonitorLogcat;
+ public static String LogCatPreferencePage_SessionFilterLogLevel;
+ public static String LogCatView_Clear_Log;
+ public static String LogCatView_Copy;
+ public static String LogCatView_Create_Filter;
+ public static String LogCatView_Create_Filter_Tooltip;
+ public static String LogCatView_Delete_Filter;
+ public static String LogCatView_Delete_Filter_Tooltip;
+ public static String LogCatView_Edit_Filter;
+ public static String LogCatView_Edit_Filter_Tooltip;
+ public static String LogCatView_Export_Selection_As_Text;
+ public static String LogCatView_Export_Selection_As_Text_Tooltip;
+ public static String LogCatView_Select_All;
+ public static String PreferencePage_ADB_Connection_Time_Out;
+ public static String PreferencePage_Adbhost_value;
+ public static String PreferencePage_Assert;
+ public static String PreferencePage_Base_Local_Debugger_Port;
+ public static String PreferencePage_Debug;
+ public static String PreferencePage_Error;
+ public static String PreferencePage_Heap_Updates_Enabled_Default;
+ public static String PreferencePage_HPROF_Action;
+ public static String PreferencePage_Info;
+ public static String PreferencePage_Logging_Level;
+ public static String PreferencePage_Open_Eclipse;
+ public static String PreferencePage_Save_Disk;
+ public static String PreferencePage_Thread_Status_Refresh_Interval;
+ public static String PreferencePage_Thread_Updates_Enabled_By_Default;
+ public static String PreferencePage_Use_Adbhost;
+ public static String PreferencePage_Verbose;
+ public static String PreferencePage_Warning;
+ public static String TableView_Copy;
+ public static String TableView_Select_All;
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/i18n/messages.properties b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/i18n/messages.properties
new file mode 100644
index 000000000..3698ebce7
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/i18n/messages.properties
@@ -0,0 +1,107 @@
+#
+# Copyright (C) 2011 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.
+#
+
+DdmsPlugin_Message_Tag_Mask_1=[%1$tF %1$tT]
+DdmsPlugin_Message_Tag_Mask_2=[%1$tF %1$tT - %2$s]
+DdmsPlugin_DDMS_Post_Create_Init=DDMS post-create init
+DeviceView_ADB_Error=Adb Error
+DeviceView_ADB_Failed_Restart=Adb failed to restart\!\n\nMake sure the plugin is properly configured.
+DeviceView_Cause_GC=Cause GC
+DeviceView_Cause_GC_Tooltip=Cause GC
+DeviceView_Debug_Process=Debug Process
+DeviceView_Debug_Process_Title=Process Debug
+DeviceView_Debug_Process_Tooltip=Debug the selected process, provided its source project is present and opened in the workspace.
+DeviceView_Debug_Session_Failed=No opened project found for %1$s. Debug session failed\!
+DeviceView_Dump_HPROF_File=Dump HPROF file
+DeviceView_Dump_HPROF_File_Not_Supported_By_VM=Dump HPROF file (not supported by this VM)
+DeviceView_Dump_HPROF_File_Tooltip=Dump HPROF file
+DeviceView_Failed_To_Save_HPROF_Data=Failed to save hprof data into temp file%1$s
+DeviceView_HPROF_Error=HPROF Error
+DeviceView_Process_Already_Being_Debugged=The process is already being debugged\!
+DeviceView_Process_Debug_Already_In_Use=The process debug port is already in use\!
+DeviceView_Pulling_From_Device=Pulling %1$s from the device
+DeviceView_Reset_ADB=Reset adb
+DeviceView_Reset_ADB_Host_Deamon=Reset the adb host daemon
+DeviceView_Save_HPROF_File=Save HPROF file
+DeviceView_Screen_Capture=Screen Capture
+DeviceView_Screen_Capture_Tooltip=Screen Capture
+DeviceView_Start_Method_Profiling=Start Method Profiling
+DeviceView_Start_Method_Profiling_Not_Suported_By_Vm=Start Method Profiling (not supported by this VM)
+DeviceView_Start_Method_Profiling_Tooltip=Start Method Profiling
+DeviceView_Stop_Method_Profiling=Stop Method Profiling
+DeviceView_Stop_Method_Profiling_Tooltip=Stop Method Profiling
+DeviceView_Stop_Process=Stop Process
+DeviceView_Stop_Process_Tooltip=Stop Process
+DeviceView_Threads=Update Threads
+DeviceView_Threads_Tooltip=Update Threads
+DeviceView_Unable_Create_HPROF_For_Application=Unable to create HPROF file for application '%1$s'.\n\n%2$s Check logcat for more information.
+DeviceView_Unable_Download_HPROF_From_Device_One_Param_First_Message=Unable to download HPROF file from device '%1$s'.
+DeviceView_Unable_Download_HPROF_From_Device_One_Param_Second_Message=Unable to download HPROF file from device '%1$s'.
+DeviceView_Unable_Download_HPROF_From_Device_Two_Param=Unable to download HPROF file from device '%1$s'.\n\n%2$s
+DeviceView_Update_Heap=Update Heap
+DeviceView_Update_Heap_Tooltip=Update Heap
+EventLogView_Clear_Log=Clear Log
+EventLogView_Clears_Event_Log=Clears the event log
+EventLogView_Import_Bug_Report_Log=Import Bug Report Log
+EventLogView_Imports_Bug_Report=Imports a bug report.
+EventLogView_Load_Log=Load Log
+EventLogView_Loads_Event_Log=Loads an event log
+EventLogView_Opens_Options_Panel=Opens the options panel
+EventLogView_Options=Options...
+EventLogView_Save_Log=Save Log
+EventLogView_Saves_Event_Log=Saves the event log
+FileExplorerView_Delete=Delete
+FileExplorerView_Delete_The_Selection=Delete the selection
+FileExplorerView_Pull_File=Pull File...
+FileExplorerView_Pull_File_From_File=Pull a file from the device
+FileExplorerView_Push_File=Push File...
+FileExplorerView_Push_File_Onto_Device=Push a file onto the device
+LogCatPreferencePage_Display_Font=Display Font:
+LogCatPreferencePage_MaxMessages=Maximum number of logcat messages to buffer:
+LogCatPreferencePage_Switch_Perspective=Switch Perspective
+LogCatPreferencePage_Switch_To=Switch to:
+LogCatPreferencePage_AutoMonitorLogcat=Monitor logcat for messages from applications in workspace
+LogCatPreferencePage_SessionFilterLogLevel=Show logcat view if message priority is atleast:
+LogCatView_Clear_Log=Clear Log
+LogCatView_Copy=Copy
+LogCatView_Create_Filter=Create Filter
+LogCatView_Create_Filter_Tooltip=Create Filter
+LogCatView_Delete_Filter=Delete Filter
+LogCatView_Delete_Filter_Tooltip=Delete Filter
+LogCatView_Edit_Filter=Edit Filter
+LogCatView_Edit_Filter_Tooltip=Edit Filter
+LogCatView_Export_Selection_As_Text=Export Selection As Text...
+LogCatView_Export_Selection_As_Text_Tooltip=Export Selection As Text...
+LogCatView_Select_All=Select All
+PreferencePage_ADB_Connection_Time_Out=ADB connection time out (ms):
+PreferencePage_Adbhost_value=ADBHOST value:
+PreferencePage_Assert=Assert
+PreferencePage_Base_Local_Debugger_Port=Base local debugger port:
+PreferencePage_Debug=Debug
+PreferencePage_Error=Error
+PreferencePage_Heap_Updates_Enabled_Default=Heap updates enabled by default
+PreferencePage_HPROF_Action=HPROF Action:
+PreferencePage_Info=Info
+PreferencePage_Logging_Level=Logging Level
+PreferencePage_Open_Eclipse=Open in Eclipse
+PreferencePage_Save_Disk=Save to disk
+PreferencePage_Thread_Status_Refresh_Interval=Thread status refresh interval (seconds):
+PreferencePage_Thread_Updates_Enabled_By_Default=Thread updates enabled by default
+PreferencePage_Use_Adbhost=Use ADBHOST
+PreferencePage_Verbose=Verbose
+PreferencePage_Warning=Warning
+TableView_Copy=Copy
+TableView_Select_All=Select All
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/preferences/LogCatColorsPage.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/preferences/LogCatColorsPage.java
new file mode 100644
index 000000000..675a51c24
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/preferences/LogCatColorsPage.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2012 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.ide.eclipse.ddms.preferences;
+
+import com.android.ddmuilib.logcat.LogCatPanel;
+import com.android.ide.eclipse.ddms.DdmsPlugin;
+
+import org.eclipse.jface.preference.ColorFieldEditor;
+import org.eclipse.jface.preference.FieldEditorPreferencePage;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+
+public class LogCatColorsPage extends FieldEditorPreferencePage
+ implements IWorkbenchPreferencePage {
+ public LogCatColorsPage() {
+ super(GRID);
+ setPreferenceStore(DdmsPlugin.getDefault().getPreferenceStore());
+ }
+
+ @Override
+ public void init(IWorkbench workbench) {
+ }
+
+ @Override
+ protected void createFieldEditors() {
+ // colors preference for different log levels
+ ColorFieldEditor cfe = new ColorFieldEditor(LogCatPanel.VERBOSE_COLOR_PREFKEY,
+ "Verbose Log Message Color", getFieldEditorParent());
+ addField(cfe);
+
+ cfe = new ColorFieldEditor(LogCatPanel.DEBUG_COLOR_PREFKEY, "Debug Log Message Color",
+ getFieldEditorParent());
+ addField(cfe);
+
+ cfe = new ColorFieldEditor(LogCatPanel.INFO_COLOR_PREFKEY, "Info Log Message Color",
+ getFieldEditorParent());
+ addField(cfe);
+
+ cfe = new ColorFieldEditor(LogCatPanel.WARN_COLOR_PREFKEY, "Warning Log Message Color",
+ getFieldEditorParent());
+ addField(cfe);
+
+ cfe = new ColorFieldEditor(LogCatPanel.ERROR_COLOR_PREFKEY, "Error Log Message Color",
+ getFieldEditorParent());
+ addField(cfe);
+
+ cfe = new ColorFieldEditor(LogCatPanel.ASSERT_COLOR_PREFKEY, "Assert Log Message Color",
+ getFieldEditorParent());
+ addField(cfe);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/preferences/LogCatPreferencePage.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/preferences/LogCatPreferencePage.java
new file mode 100644
index 000000000..aa88eecb7
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/preferences/LogCatPreferencePage.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2007 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.ide.eclipse.ddms.preferences;
+
+import com.android.ddmlib.Log.LogLevel;
+import com.android.ddmuilib.logcat.LogCatMessageList;
+import com.android.ddmuilib.logcat.LogCatPanel;
+import com.android.ide.eclipse.base.InstallDetails;
+import com.android.ide.eclipse.ddms.DdmsPlugin;
+import com.android.ide.eclipse.ddms.LogCatMonitor;
+import com.android.ide.eclipse.ddms.i18n.Messages;
+
+import org.eclipse.jface.preference.BooleanFieldEditor;
+import org.eclipse.jface.preference.ComboFieldEditor;
+import org.eclipse.jface.preference.FieldEditorPreferencePage;
+import org.eclipse.jface.preference.FontFieldEditor;
+import org.eclipse.jface.preference.IntegerFieldEditor;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.ui.IPerspectiveDescriptor;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * Preference Pane for LogCat.
+ */
+public class LogCatPreferencePage extends FieldEditorPreferencePage implements
+ IWorkbenchPreferencePage {
+ private BooleanFieldEditor mSwitchPerspective;
+ private ComboFieldEditor mWhichPerspective;
+ private IntegerFieldEditor mMaxMessages;
+ private BooleanFieldEditor mAutoMonitorLogcat;
+ private ComboFieldEditor mAutoMonitorLogcatLevel;
+ private BooleanFieldEditor mAutoScrollLock;
+
+ public LogCatPreferencePage() {
+ super(GRID);
+ setPreferenceStore(DdmsPlugin.getDefault().getPreferenceStore());
+ }
+
+ @Override
+ protected void createFieldEditors() {
+ FontFieldEditor ffe = new FontFieldEditor(LogCatPanel.LOGCAT_VIEW_FONT_PREFKEY,
+ Messages.LogCatPreferencePage_Display_Font, getFieldEditorParent());
+ addField(ffe);
+
+ mMaxMessages = new IntegerFieldEditor(
+ LogCatMessageList.MAX_MESSAGES_PREFKEY,
+ Messages.LogCatPreferencePage_MaxMessages, getFieldEditorParent());
+ addField(mMaxMessages);
+
+ mAutoScrollLock = new BooleanFieldEditor(LogCatPanel.AUTO_SCROLL_LOCK_PREFKEY,
+ "Automatically enable/disable scroll lock based on the scrollbar position",
+ getFieldEditorParent());
+ addField(mAutoScrollLock);
+
+ createHorizontalSeparator();
+
+ if (InstallDetails.isAdtInstalled()) {
+ createAdtSpecificFieldEditors();
+ }
+ }
+
+ private void createHorizontalSeparator() {
+ Label l = new Label(getFieldEditorParent(), SWT.SEPARATOR | SWT.HORIZONTAL);
+ GridData gd = new GridData(GridData.FILL_HORIZONTAL);
+ gd.horizontalSpan = 3;
+ l.setLayoutData(gd);
+ }
+
+ private void createAdtSpecificFieldEditors() {
+ mSwitchPerspective = new BooleanFieldEditor(PreferenceInitializer.ATTR_SWITCH_PERSPECTIVE,
+ Messages.LogCatPreferencePage_Switch_Perspective, getFieldEditorParent());
+ addField(mSwitchPerspective);
+ IPerspectiveDescriptor[] perspectiveDescriptors =
+ PlatformUI.getWorkbench().getPerspectiveRegistry().getPerspectives();
+ String[][] perspectives = new String[0][0];
+ if (perspectiveDescriptors.length > 0) {
+ perspectives = new String[perspectiveDescriptors.length][2];
+ for (int i = 0; i < perspectiveDescriptors.length; i++) {
+ IPerspectiveDescriptor perspective = perspectiveDescriptors[i];
+ perspectives[i][0] = perspective.getLabel();
+ perspectives[i][1] = perspective.getId();
+ }
+ }
+ mWhichPerspective = new ComboFieldEditor(PreferenceInitializer.ATTR_PERSPECTIVE_ID,
+ Messages.LogCatPreferencePage_Switch_To, perspectives, getFieldEditorParent());
+ mWhichPerspective.setEnabled(getPreferenceStore()
+ .getBoolean(PreferenceInitializer.ATTR_SWITCH_PERSPECTIVE), getFieldEditorParent());
+ addField(mWhichPerspective);
+
+ createHorizontalSeparator();
+
+ mAutoMonitorLogcat = new BooleanFieldEditor(LogCatMonitor.AUTO_MONITOR_PREFKEY,
+ Messages.LogCatPreferencePage_AutoMonitorLogcat,
+ getFieldEditorParent());
+ addField(mAutoMonitorLogcat);
+
+ mAutoMonitorLogcatLevel = new ComboFieldEditor(LogCatMonitor.AUTO_MONITOR_LOGLEVEL,
+ Messages.LogCatPreferencePage_SessionFilterLogLevel,
+ new String[][] {
+ { LogLevel.VERBOSE.toString(), LogLevel.VERBOSE.getStringValue() },
+ { LogLevel.DEBUG.toString(), LogLevel.DEBUG.getStringValue() },
+ { LogLevel.INFO.toString(), LogLevel.INFO.getStringValue() },
+ { LogLevel.WARN.toString(), LogLevel.WARN.getStringValue() },
+ { LogLevel.ERROR.toString(), LogLevel.ERROR.getStringValue() },
+ { LogLevel.ASSERT.toString(), LogLevel.ASSERT.getStringValue() },
+ },
+ getFieldEditorParent());
+ mAutoMonitorLogcatLevel.setEnabled(
+ getPreferenceStore().getBoolean(LogCatMonitor.AUTO_MONITOR_PREFKEY),
+ getFieldEditorParent());
+ addField(mAutoMonitorLogcatLevel);
+ }
+
+ @Override
+ public void init(IWorkbench workbench) {
+ }
+
+ @Override
+ public void propertyChange(PropertyChangeEvent event) {
+ if (event.getSource().equals(mSwitchPerspective)) {
+ mWhichPerspective.setEnabled(mSwitchPerspective.getBooleanValue(),
+ getFieldEditorParent());
+ } else if (event.getSource().equals(mAutoMonitorLogcat)) {
+ mAutoMonitorLogcatLevel.setEnabled(mAutoMonitorLogcat.getBooleanValue(),
+ getFieldEditorParent());
+ }
+ }
+
+ @Override
+ protected void performDefaults() {
+ super.performDefaults();
+ mWhichPerspective.setEnabled(mSwitchPerspective.getBooleanValue(), getFieldEditorParent());
+
+ mMaxMessages.setStringValue(
+ Integer.toString(LogCatMessageList.MAX_MESSAGES_DEFAULT));
+
+ mAutoMonitorLogcatLevel.setEnabled(mAutoMonitorLogcat.getBooleanValue(),
+ getFieldEditorParent());
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/preferences/PreferenceInitializer.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/preferences/PreferenceInitializer.java
new file mode 100644
index 000000000..254b2c58a
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/preferences/PreferenceInitializer.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2007 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.ide.eclipse.ddms.preferences;
+
+import com.android.ddmlib.DdmPreferences;
+import com.android.ddmlib.Log.LogLevel;
+import com.android.ddmuilib.DdmUiPreferences;
+import com.android.ide.eclipse.ddms.DdmsPlugin;
+import com.android.ide.eclipse.ddms.LogCatMonitor;
+import com.android.ide.eclipse.ddms.views.DeviceView.HProfHandler;
+import com.android.ide.eclipse.ddms.views.LogCatView;
+
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.content.IContentType;
+import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.FontData;
+
+/**
+ * Class used to initialize default preference values.
+ */
+public class PreferenceInitializer extends AbstractPreferenceInitializer {
+
+ public final static String ATTR_LOG_LEVEL =
+ DdmsPlugin.PLUGIN_ID + ".logLevel"; //$NON-NLS-1$
+
+ public final static String ATTR_DEBUG_PORT_BASE =
+ DdmsPlugin.PLUGIN_ID + ".adbDebugBasePort"; //$NON-NLS-1$
+
+ public final static String ATTR_SELECTED_DEBUG_PORT =
+ DdmsPlugin.PLUGIN_ID + ".debugSelectedPort"; //$NON-NLS-1$
+
+ public final static String ATTR_DEFAULT_THREAD_UPDATE =
+ DdmsPlugin.PLUGIN_ID + ".defaultThreadUpdateEnabled"; //$NON-NLS-1$
+
+ public final static String ATTR_DEFAULT_HEAP_UPDATE =
+ DdmsPlugin.PLUGIN_ID + ".defaultHeapUpdateEnabled"; //$NON-NLS-1$
+
+ public final static String ATTR_THREAD_INTERVAL =
+ DdmsPlugin.PLUGIN_ID + ".threadStatusInterval"; //$NON-NLS-1$
+
+ public final static String ATTR_IMAGE_SAVE_DIR =
+ DdmsPlugin.PLUGIN_ID + ".imageSaveDir"; //$NON-NLS-1$
+
+ public final static String ATTR_LAST_IMAGE_SAVE_DIR =
+ DdmsPlugin.PLUGIN_ID + ".lastImageSaveDir"; //$NON-NLS-1$
+
+ public final static String ATTR_LOGCAT_FONT =
+ DdmsPlugin.PLUGIN_ID + ".logcatFont"; //$NON-NLS-1$
+
+ public final static String ATTR_HPROF_ACTION =
+ DdmsPlugin.PLUGIN_ID + ".hprofAction"; //$NON-NLS-1$
+
+ public final static String ATTR_TIME_OUT =
+ DdmsPlugin.PLUGIN_ID + ".timeOut"; //$NON-NLS-1$
+
+ public final static String ATTR_USE_ADBHOST =
+ DdmsPlugin.PLUGIN_ID + ".useAdbHost"; //$NON-NLS-1$
+
+ public final static String ATTR_ADBHOST_VALUE =
+ DdmsPlugin.PLUGIN_ID + ".adbHostValue"; //$NON-NLS-1$
+
+ public final static String ATTR_SWITCH_PERSPECTIVE =
+ DdmsPlugin.PLUGIN_ID + ".switchPerspective"; //$NON-NLS-1$
+
+ public final static String ATTR_PERSPECTIVE_ID =
+ DdmsPlugin.PLUGIN_ID + ".perspectiveId"; //$NON-NLS-1$
+
+ public static final String ATTR_PROFILER_BUFSIZE_MB =
+ DdmsPlugin.PLUGIN_ID + ".profilerBufferSizeMb"; //$NON-NLS-1$
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer
+ * #initializeDefaultPreferences()
+ */
+ @Override
+ public void initializeDefaultPreferences() {
+ IPreferenceStore store = DdmsPlugin.getDefault().getPreferenceStore();
+
+ store.setDefault(ATTR_DEBUG_PORT_BASE, DdmPreferences.DEFAULT_DEBUG_PORT_BASE);
+
+ store.setDefault(ATTR_SELECTED_DEBUG_PORT, DdmPreferences.DEFAULT_SELECTED_DEBUG_PORT);
+
+ store.setDefault(ATTR_DEFAULT_THREAD_UPDATE, DdmPreferences.DEFAULT_INITIAL_THREAD_UPDATE);
+ store.setDefault(ATTR_DEFAULT_HEAP_UPDATE,
+ DdmPreferences.DEFAULT_INITIAL_HEAP_UPDATE);
+
+ store.setDefault(ATTR_PROFILER_BUFSIZE_MB, DdmPreferences.DEFAULT_PROFILER_BUFFER_SIZE_MB);
+
+ store.setDefault(ATTR_THREAD_INTERVAL, DdmUiPreferences.DEFAULT_THREAD_REFRESH_INTERVAL);
+
+ String homeDir = System.getProperty("user.home"); //$NON-NLS-1$
+ store.setDefault(ATTR_IMAGE_SAVE_DIR, homeDir);
+
+ store.setDefault(ATTR_LOG_LEVEL, DdmPreferences.DEFAULT_LOG_LEVEL.getStringValue());
+
+ store.setDefault(ATTR_LOGCAT_FONT,
+ new FontData("Courier", 10, SWT.NORMAL).toString()); //$NON-NLS-1$
+
+ // When obtaining hprof files from the device, default to opening the file
+ // only if there is a registered content type for the hprof extension.
+ store.setDefault(ATTR_HPROF_ACTION, HProfHandler.ACTION_SAVE);
+ for (IContentType contentType: Platform.getContentTypeManager().getAllContentTypes()) {
+ if (contentType.isAssociatedWith(HProfHandler.DOT_HPROF)) {
+ store.setDefault(ATTR_HPROF_ACTION, HProfHandler.ACTION_OPEN);
+ break;
+ }
+ }
+
+ store.setDefault(ATTR_TIME_OUT, DdmPreferences.DEFAULT_TIMEOUT);
+
+ store.setDefault(ATTR_USE_ADBHOST, DdmPreferences.DEFAULT_USE_ADBHOST);
+ store.setDefault(ATTR_ADBHOST_VALUE, DdmPreferences.DEFAULT_ADBHOST_VALUE);
+ store.setDefault(ATTR_SWITCH_PERSPECTIVE, LogCatView.DEFAULT_SWITCH_PERSPECTIVE);
+ store.setDefault(ATTR_PERSPECTIVE_ID, LogCatView.DEFAULT_PERSPECTIVE_ID);
+
+ store.setDefault(LogCatMonitor.AUTO_MONITOR_PREFKEY, true);
+ store.setDefault(LogCatMonitor.AUTO_MONITOR_LOGLEVEL, LogLevel.VERBOSE.getStringValue());
+ }
+
+ /**
+ * Initializes the preferences of ddmlib and ddmuilib with values from the eclipse store.
+ */
+ public synchronized static void setupPreferences() {
+ IPreferenceStore store = DdmsPlugin.getDefault().getPreferenceStore();
+
+ DdmPreferences.setDebugPortBase(store.getInt(ATTR_DEBUG_PORT_BASE));
+ DdmPreferences.setSelectedDebugPort(store.getInt(ATTR_SELECTED_DEBUG_PORT));
+ DdmPreferences.setLogLevel(store.getString(ATTR_LOG_LEVEL));
+ DdmPreferences.setInitialThreadUpdate(store.getBoolean(ATTR_DEFAULT_THREAD_UPDATE));
+ DdmPreferences.setInitialHeapUpdate(store.getBoolean(ATTR_DEFAULT_HEAP_UPDATE));
+ DdmPreferences.setProfilerBufferSizeMb(store.getInt(ATTR_PROFILER_BUFSIZE_MB));
+ DdmUiPreferences.setThreadRefreshInterval(store.getInt(ATTR_THREAD_INTERVAL));
+ DdmPreferences.setTimeOut(store.getInt(ATTR_TIME_OUT));
+ DdmPreferences.setUseAdbHost(store.getBoolean(ATTR_USE_ADBHOST));
+ DdmPreferences.setAdbHostValue(store.getString(ATTR_ADBHOST_VALUE));
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/preferences/PreferencePage.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/preferences/PreferencePage.java
new file mode 100644
index 000000000..56af601e5
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/preferences/PreferencePage.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2007 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.ide.eclipse.ddms.preferences;
+
+import com.android.ddmlib.DdmPreferences;
+import com.android.ddmlib.Log.LogLevel;
+import com.android.ide.eclipse.base.InstallDetails;
+import com.android.ide.eclipse.ddms.DdmsPlugin;
+import com.android.ide.eclipse.ddms.i18n.Messages;
+import com.android.ide.eclipse.ddms.views.DeviceView.HProfHandler;
+
+import org.eclipse.jface.preference.BooleanFieldEditor;
+import org.eclipse.jface.preference.ComboFieldEditor;
+import org.eclipse.jface.preference.FieldEditorPreferencePage;
+import org.eclipse.jface.preference.IntegerFieldEditor;
+import org.eclipse.jface.preference.RadioGroupFieldEditor;
+import org.eclipse.jface.preference.StringFieldEditor;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+
+public class PreferencePage extends FieldEditorPreferencePage implements
+ IWorkbenchPreferencePage {
+
+ private BooleanFieldEditor mUseAdbHost;
+ private StringFieldEditor mAdbHostValue;
+ private IntegerFieldEditor mProfilerBufsize;
+
+ public PreferencePage() {
+ super(GRID);
+ setPreferenceStore(DdmsPlugin.getDefault().getPreferenceStore());
+ }
+
+ /**
+ * Creates the field editors. Field editors are abstractions of the common
+ * GUI blocks needed to manipulate various types of preferences. Each field
+ * editor knows how to save and restore itself.
+ */
+ @Override
+ public void createFieldEditors() {
+ IntegerFieldEditor ife;
+
+ ife = new IntegerFieldEditor(PreferenceInitializer.ATTR_DEBUG_PORT_BASE,
+ Messages.PreferencePage_Base_Local_Debugger_Port, getFieldEditorParent());
+ ife.setValidRange(1024, 32767);
+ addField(ife);
+
+ BooleanFieldEditor bfe;
+
+ bfe = new BooleanFieldEditor(PreferenceInitializer.ATTR_DEFAULT_THREAD_UPDATE,
+ Messages.PreferencePage_Thread_Updates_Enabled_By_Default, getFieldEditorParent());
+ addField(bfe);
+
+ bfe = new BooleanFieldEditor(PreferenceInitializer.ATTR_DEFAULT_HEAP_UPDATE,
+ Messages.PreferencePage_Heap_Updates_Enabled_Default, getFieldEditorParent());
+ addField(bfe);
+
+ ife = new IntegerFieldEditor(PreferenceInitializer.ATTR_THREAD_INTERVAL,
+ Messages.PreferencePage_Thread_Status_Refresh_Interval, getFieldEditorParent());
+ ife.setValidRange(1, 60);
+ addField(ife);
+
+ if (InstallDetails.isAdtInstalled()) {
+ ComboFieldEditor cfe = new ComboFieldEditor(PreferenceInitializer.ATTR_HPROF_ACTION,
+ Messages.PreferencePage_HPROF_Action, new String[][] {
+ {
+ Messages.PreferencePage_Save_Disk, HProfHandler.ACTION_SAVE
+ },
+ {
+ Messages.PreferencePage_Open_Eclipse, HProfHandler.ACTION_OPEN
+ },
+ }, getFieldEditorParent());
+ addField(cfe);
+ }
+
+ mProfilerBufsize = new IntegerFieldEditor(PreferenceInitializer.ATTR_PROFILER_BUFSIZE_MB,
+ "Method Profiler buffer size (MB):",
+ getFieldEditorParent());
+ addField(mProfilerBufsize);
+
+ ife = new IntegerFieldEditor(PreferenceInitializer.ATTR_TIME_OUT,
+ Messages.PreferencePage_ADB_Connection_Time_Out, getFieldEditorParent());
+ addField(ife);
+
+ RadioGroupFieldEditor rgfe = new RadioGroupFieldEditor(
+ PreferenceInitializer.ATTR_LOG_LEVEL,
+ Messages.PreferencePage_Logging_Level, 1, new String[][] {
+ {
+ Messages.PreferencePage_Verbose, LogLevel.VERBOSE.getStringValue()
+ },
+ {
+ Messages.PreferencePage_Debug, LogLevel.DEBUG.getStringValue()
+ },
+ {
+ Messages.PreferencePage_Info, LogLevel.INFO.getStringValue()
+ },
+ {
+ Messages.PreferencePage_Warning, LogLevel.WARN.getStringValue()
+ },
+ {
+ Messages.PreferencePage_Error, LogLevel.ERROR.getStringValue()
+ },
+ {
+ Messages.PreferencePage_Assert, LogLevel.ASSERT.getStringValue()
+ }
+ },
+ getFieldEditorParent(), true);
+ addField(rgfe);
+ mUseAdbHost = new BooleanFieldEditor(PreferenceInitializer.ATTR_USE_ADBHOST,
+ Messages.PreferencePage_Use_Adbhost, getFieldEditorParent());
+ addField(mUseAdbHost);
+ mAdbHostValue = new StringFieldEditor(PreferenceInitializer.ATTR_ADBHOST_VALUE,
+ Messages.PreferencePage_Adbhost_value, getFieldEditorParent());
+ mAdbHostValue.setEnabled(getPreferenceStore()
+ .getBoolean(PreferenceInitializer.ATTR_USE_ADBHOST), getFieldEditorParent());
+ addField(mAdbHostValue);
+ }
+
+ @Override
+ public void init(IWorkbench workbench) {
+ }
+
+ @Override
+ public void propertyChange(PropertyChangeEvent event) {
+ if (event.getSource().equals(mUseAdbHost)) {
+ mAdbHostValue.setEnabled(mUseAdbHost.getBooleanValue(), getFieldEditorParent());
+ } else if (event.getSource().equals(mProfilerBufsize)) {
+ DdmPreferences.setProfilerBufferSizeMb(mProfilerBufsize.getIntValue());
+ }
+ super.propertyChange(event);
+ }
+
+ @Override
+ protected void performDefaults() {
+ super.performDefaults();
+ mAdbHostValue.setEnabled(mUseAdbHost.getBooleanValue(), getFieldEditorParent());
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/ISystraceOptions.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/ISystraceOptions.java
new file mode 100644
index 000000000..f0e080421
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/ISystraceOptions.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2012 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.ide.eclipse.ddms.systrace;
+
+public interface ISystraceOptions {
+ /** Get tags to enable, returns null if no tags need to be enabled. */
+ String getTags();
+
+ /** Get the command line options to atrace. */
+ String getOptions();
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/ISystraceOptionsDialog.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/ISystraceOptionsDialog.java
new file mode 100644
index 000000000..4cc0faa59
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/ISystraceOptionsDialog.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2012 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.ide.eclipse.ddms.systrace;
+
+public interface ISystraceOptionsDialog {
+ ISystraceOptions getSystraceOptions();
+ String getTraceFilePath();
+ int open();
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceOptionsDialogV1.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceOptionsDialogV1.java
new file mode 100644
index 000000000..b462ada29
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceOptionsDialogV1.java
@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) 2012 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.ide.eclipse.ddms.systrace;
+
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+import java.io.File;
+
+public class SystraceOptionsDialogV1 extends TitleAreaDialog implements ISystraceOptionsDialog {
+ private static final String TITLE = "Android System Trace";
+ private static final String DEFAULT_MESSAGE =
+ "Settings to use while capturing system level trace";
+ private static final String DEFAULT_TRACE_FNAME = "trace.html"; //$NON-NLS-1$
+
+ private Text mDestinationText;
+ private String mDestinationPath;
+ private Text mTraceDurationText;
+ private Text mTraceBufferSizeText;
+
+ private static String sSaveToFolder = System.getProperty("user.home"); //$NON-NLS-1$
+ private static String sTraceDuration = "";
+ private static String sTraceBufferSize = "";
+
+ private Button mTraceCpuFreqBtn;
+ private Button mTraceCpuIdleBtn;
+ private Button mTraceCpuLoadBtn;
+ private Button mTraceDiskIoBtn;
+ private Button mTraceKernelWorkqueuesBtn;
+ private Button mTraceCpuSchedulerBtn;
+
+ private static boolean sTraceCpuFreq;
+ private static boolean sTraceCpuIdle;
+ private static boolean sTraceCpuLoad;
+ private static boolean sTraceDiskIo;
+ private static boolean sTraceKernelWorkqueues;
+ private static boolean sTraceCpuScheduler;
+
+ private Button mGfxTagBtn;
+ private Button mInputTagBtn;
+ private Button mViewTagBtn;
+ private Button mWebViewTagBtn;
+ private Button mWmTagBtn;
+ private Button mAmTagBtn;
+ private Button mSyncTagBtn;
+ private Button mAudioTagBtn;
+ private Button mVideoTagBtn;
+ private Button mCameraTagBtn;
+
+ private static boolean sGfxTag;
+ private static boolean sInputTag;
+ private static boolean sViewTag;
+ private static boolean sWebViewTag;
+ private static boolean sWmTag;
+ private static boolean sAmTag;
+ private static boolean sSyncTag;
+ private static boolean sAudioTag;
+ private static boolean sVideoTag;
+ private static boolean sCameraTag;
+
+ private final SystraceOptions mOptions = new SystraceOptions();
+
+ public SystraceOptionsDialogV1(Shell parentShell) {
+ super(parentShell);
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ setTitle(TITLE);
+ setMessage(DEFAULT_MESSAGE);
+
+ Composite c = new Composite(parent, SWT.BORDER);
+ c.setLayout(new GridLayout(3, false));
+ c.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ Label l = new Label(c, SWT.NONE);
+ l.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+ l.setText("Destination File: ");
+
+ mDestinationText = new Text(c, SWT.BORDER);
+ mDestinationText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+ mDestinationText.setText(sSaveToFolder + File.separator + DEFAULT_TRACE_FNAME);
+
+ final Button browse = new Button(c, SWT.NONE);
+ browse.setText("Browse...");
+ browse.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ String path = openBrowseDialog(browse.getShell());
+ if (path != null) mDestinationText.setText(path);
+ }
+ });
+
+ Label lblTraceDurationseconds = new Label(c, SWT.NONE);
+ lblTraceDurationseconds.setLayoutData(
+ new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+ lblTraceDurationseconds.setText("Trace duration (seconds): ");
+
+ mTraceDurationText = new Text(c, SWT.BORDER);
+ mTraceDurationText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
+ mTraceDurationText.setText(sTraceDuration);
+
+ Label lblTraceBufferSize = new Label(c, SWT.NONE);
+ lblTraceBufferSize.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+ lblTraceBufferSize.setText("Trace Buffer Size (kb): ");
+
+ mTraceBufferSizeText = new Text(c, SWT.BORDER);
+ mTraceBufferSizeText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
+ mTraceBufferSizeText.setText(sTraceBufferSize);
+
+ Label separator = new Label(c, SWT.SEPARATOR | SWT.HORIZONTAL);
+ GridData gd = new GridData(GridData.FILL_HORIZONTAL);
+ gd.horizontalSpan = 3;
+ separator.setLayoutData(gd);
+
+ Group grpTraceEvents = new Group(c, SWT.BORDER);
+ grpTraceEvents.setLayout(new GridLayout(3, false));
+ grpTraceEvents.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 2, 1));
+ grpTraceEvents.setText("Trace Events");
+
+ mTraceCpuFreqBtn = new Button(grpTraceEvents, SWT.CHECK);
+ mTraceCpuFreqBtn.setText("CPU Frequency Changes");
+ mTraceCpuFreqBtn.setSelection(sTraceCpuFreq);
+
+ mTraceCpuIdleBtn = new Button(grpTraceEvents, SWT.CHECK);
+ mTraceCpuIdleBtn.setText("CPU Idle Events");
+ mTraceCpuIdleBtn.setSelection(sTraceCpuIdle);
+
+ mTraceCpuLoadBtn = new Button(grpTraceEvents, SWT.CHECK);
+ mTraceCpuLoadBtn.setText("CPU Load");
+ mTraceCpuLoadBtn.setSelection(sTraceCpuLoad);
+
+ mTraceCpuSchedulerBtn = new Button(grpTraceEvents, SWT.CHECK);
+ mTraceCpuSchedulerBtn.setText("CPU Scheduler");
+ mTraceCpuSchedulerBtn.setSelection(sTraceCpuScheduler);
+
+ Group grpTraceRootEvents = new Group(c, SWT.BORDER);
+ grpTraceRootEvents.setLayout(new GridLayout(2, false));
+ grpTraceRootEvents.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 2, 1));
+ grpTraceRootEvents.setText("Trace Events that require root privileges on device");
+
+ mTraceDiskIoBtn = new Button(grpTraceRootEvents, SWT.CHECK);
+ mTraceDiskIoBtn.setText("Disk I/O");
+ mTraceDiskIoBtn.setSelection(sTraceDiskIo);
+
+ mTraceKernelWorkqueuesBtn = new Button(grpTraceRootEvents, SWT.CHECK);
+ mTraceKernelWorkqueuesBtn.setText("Kernel Workqueues (requires root)");
+ mTraceKernelWorkqueuesBtn.setSelection(sTraceKernelWorkqueues);
+
+ Group grpTraceTags = new Group(c, SWT.BORDER);
+ grpTraceTags.setLayout(new GridLayout(5, false));
+ grpTraceTags.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 3, 1));
+ grpTraceTags.setText("Trace Tags");
+
+ mGfxTagBtn = new Button(grpTraceTags, SWT.CHECK);
+ mGfxTagBtn.setText("gfx");
+ mGfxTagBtn.setSelection(sGfxTag);
+
+ mInputTagBtn = new Button(grpTraceTags, SWT.CHECK);
+ mInputTagBtn.setText("input");
+ mInputTagBtn.setSelection(sInputTag);
+
+ mViewTagBtn = new Button(grpTraceTags, SWT.CHECK);
+ mViewTagBtn.setText("view");
+ mViewTagBtn.setSelection(sViewTag);
+
+ mWebViewTagBtn = new Button(grpTraceTags, SWT.CHECK);
+ mWebViewTagBtn.setText("webview");
+ mWebViewTagBtn.setSelection(sWebViewTag);
+
+ mWmTagBtn = new Button(grpTraceTags, SWT.CHECK);
+ mWmTagBtn.setText("wm");
+ mWmTagBtn.setSelection(sWmTag);
+
+ mAmTagBtn = new Button(grpTraceTags, SWT.CHECK);
+ mAmTagBtn.setText("am");
+ mAmTagBtn.setSelection(sAmTag);
+
+ mSyncTagBtn = new Button(grpTraceTags, SWT.CHECK);
+ mSyncTagBtn.setText("sync");
+ mSyncTagBtn.setSelection(sSyncTag);
+
+ mAudioTagBtn = new Button(grpTraceTags, SWT.CHECK);
+ mAudioTagBtn.setText("audio");
+ mAudioTagBtn.setSelection(sAudioTag);
+
+ mVideoTagBtn = new Button(grpTraceTags, SWT.CHECK);
+ mVideoTagBtn.setText("video");
+ mVideoTagBtn.setSelection(sVideoTag);
+
+ mCameraTagBtn = new Button(grpTraceTags, SWT.CHECK);
+ mCameraTagBtn.setText("camera");
+ mCameraTagBtn.setSelection(sCameraTag);
+
+ Label lblTraceTagsWarning = new Label(grpTraceTags, SWT.NONE);
+ lblTraceTagsWarning.setText(
+ "Changes to trace tags will likely need a restart of the Android framework to take effect:\n"
+ + " $ adb shell stop\n"
+ + " $ adb shell start");
+ lblTraceTagsWarning.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 5, 1));
+
+ ModifyListener m = new ModifyListener() {
+ @Override
+ public void modifyText(ModifyEvent e) {
+ validateFields();
+ }
+ };
+
+ mDestinationText.addModifyListener(m);
+ mTraceBufferSizeText.addModifyListener(m);
+ mTraceDurationText.addModifyListener(m);
+
+ return c;
+ }
+
+ private void validateFields() {
+ // validate trace destination path
+ String msg = validatePath(mDestinationText.getText());
+ if (msg != null) {
+ setErrorMessage(msg);
+ getButton(OK).setEnabled(false);
+ return;
+ }
+
+ // validate the trace duration
+ if (!validateInteger(mTraceDurationText.getText())) {
+ setErrorMessage("Trace Duration should be a valid integer (seconds)");
+ getButton(OK).setEnabled(false);
+ return;
+ }
+
+ // validate the trace buffer size
+ if (!validateInteger(mTraceBufferSizeText.getText())) {
+ setErrorMessage("Trace Buffer Size should be a valid integer (kilobytes)");
+ getButton(OK).setEnabled(false);
+ return;
+ }
+
+ getButton(OK).setEnabled(true);
+ setErrorMessage(null);
+ }
+
+ private boolean validateInteger(String text) {
+ if (text == null || text.isEmpty()) {
+ return true;
+ }
+
+ try {
+ Integer.parseInt(text);
+ return true;
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+
+ private String validatePath(String path) {
+ if (path == null || path.isEmpty()) {
+ return null;
+ }
+
+ File f = new File(path);
+ if (f.isDirectory()) {
+ return String.format("The path '%s' points to a folder", path);
+ }
+
+ if (!f.exists()) { // if such a file doesn't exist, make sure the parent folder is valid
+ if (!f.getParentFile().isDirectory()) {
+ return String.format("That path '%s' is not a valid folder.", f.getParent());
+ }
+ }
+
+ return null;
+ }
+
+ private String openBrowseDialog(Shell parentShell) {
+ FileDialog fd = new FileDialog(parentShell, SWT.SAVE);
+
+ fd.setText("Save To");
+ fd.setFileName(DEFAULT_TRACE_FNAME);
+
+ fd.setFilterPath(sSaveToFolder);
+ fd.setFilterExtensions(new String[] { "*.html" }); //$NON-NLS-1$
+
+ String fname = fd.open();
+ if (fname == null || fname.trim().length() == 0) {
+ return null;
+ }
+
+ sSaveToFolder = fd.getFilterPath();
+ return fname;
+ }
+
+ @Override
+ protected void okPressed() {
+ mDestinationPath = mDestinationText.getText().trim();
+
+ sTraceDuration = mTraceDurationText.getText();
+ if (!sTraceDuration.isEmpty()) {
+ mOptions.mTraceDuration = Integer.parseInt(sTraceDuration);
+ }
+
+ sTraceBufferSize = mTraceBufferSizeText.getText();
+ if (!sTraceBufferSize.isEmpty()) {
+ mOptions.mTraceBufferSize = Integer.parseInt(sTraceBufferSize);
+ }
+
+ mOptions.mTraceCpuFreq = mTraceCpuFreqBtn.getSelection();
+ mOptions.mTraceCpuIdle = mTraceCpuIdleBtn.getSelection();
+ mOptions.mTraceCpuLoad = mTraceCpuLoadBtn.getSelection();
+ mOptions.mTraceDiskIo = mTraceDiskIoBtn.getSelection();
+ mOptions.mTraceKernelWorkqueues = mTraceKernelWorkqueuesBtn.getSelection();
+ mOptions.mTraceCpuScheduler = mTraceCpuSchedulerBtn.getSelection();
+
+ if (mGfxTagBtn.getSelection()) mOptions.enableTag(SystraceOptions.TAG_GFX);
+ if (mInputTagBtn.getSelection()) mOptions.enableTag(SystraceOptions.TAG_INPUT);
+ if (mViewTagBtn.getSelection()) mOptions.enableTag(SystraceOptions.TAG_VIEW);
+ if (mWebViewTagBtn.getSelection()) mOptions.enableTag(SystraceOptions.TAG_WEBVIEW);
+ if (mWmTagBtn.getSelection()) mOptions.enableTag(SystraceOptions.TAG_WM);
+ if (mAmTagBtn.getSelection()) mOptions.enableTag(SystraceOptions.TAG_AM);
+ if (mSyncTagBtn.getSelection()) mOptions.enableTag(SystraceOptions.TAG_SYNC);
+ if (mAudioTagBtn.getSelection()) mOptions.enableTag(SystraceOptions.TAG_AUDIO);
+ if (mVideoTagBtn.getSelection()) mOptions.enableTag(SystraceOptions.TAG_VIDEO);
+ if (mCameraTagBtn.getSelection()) mOptions.enableTag(SystraceOptions.TAG_CAMERA);
+
+ // save current selections to be restored if the dialog is invoked again
+ sTraceCpuFreq = mTraceCpuFreqBtn.getSelection();
+ sTraceCpuIdle = mTraceCpuIdleBtn.getSelection();
+ sTraceCpuLoad = mTraceCpuLoadBtn.getSelection();
+ sTraceDiskIo = mTraceDiskIoBtn.getSelection();
+ sTraceKernelWorkqueues = mTraceKernelWorkqueuesBtn.getSelection();
+ sTraceCpuScheduler = mTraceCpuSchedulerBtn.getSelection();
+
+ sGfxTag = mGfxTagBtn.getSelection();
+ sInputTag = mInputTagBtn.getSelection();
+ sViewTag = mViewTagBtn.getSelection();
+ sWebViewTag = mWebViewTagBtn.getSelection();
+ sWmTag = mWmTagBtn.getSelection();
+ sAmTag = mAmTagBtn.getSelection();
+ sSyncTag = mSyncTagBtn.getSelection();
+ sAudioTag = mAudioTagBtn.getSelection();
+ sVideoTag = mVideoTagBtn.getSelection();
+ sCameraTag = mCameraTagBtn.getSelection();
+
+ super.okPressed();
+ }
+
+ @Override
+ public SystraceOptions getSystraceOptions() {
+ return mOptions;
+ }
+
+ @Override
+ public String getTraceFilePath() {
+ return mDestinationPath;
+ }
+
+ private class SystraceOptions implements ISystraceOptions {
+ // This list is based on the tags in frameworks/native/include/utils/Trace.h
+ private static final int TAG_GFX = 1 << 1;
+ private static final int TAG_INPUT = 1 << 2;
+ private static final int TAG_VIEW = 1 << 3;
+ private static final int TAG_WEBVIEW = 1 << 4;
+ private static final int TAG_WM = 1 << 5;
+ private static final int TAG_AM = 1 << 6;
+ private static final int TAG_SYNC = 1 << 7;
+ private static final int TAG_AUDIO = 1 << 8;
+ private static final int TAG_VIDEO = 1 << 9;
+ private static final int TAG_CAMERA = 1 << 10;
+
+ private int mTraceBufferSize;
+ private int mTraceDuration;
+
+ private boolean mTraceCpuFreq;
+ private boolean mTraceCpuIdle;
+ private boolean mTraceCpuLoad;
+ private boolean mTraceDiskIo;
+ private boolean mTraceKernelWorkqueues;
+ private boolean mTraceCpuScheduler;
+
+ private int mTag;
+
+ private void enableTag(int tag) {
+ mTag |= tag;
+ }
+
+ @Override
+ public String getTags() {
+ return mTag == 0 ? null : "0x" + Integer.toHexString(mTag);
+ }
+
+ @Override
+ public String getOptions() {
+ StringBuilder sb = new StringBuilder(20);
+
+ if (mTraceCpuFreq) sb.append("-f "); //$NON-NLS-1$
+ if (mTraceCpuIdle) sb.append("-i "); //$NON-NLS-1$
+ if (mTraceCpuLoad) sb.append("-l "); //$NON-NLS-1$
+ if (mTraceDiskIo) sb.append("-d "); //$NON-NLS-1$
+ if (mTraceKernelWorkqueues) sb.append("-w "); //$NON-NLS-1$
+ if (mTraceCpuScheduler) sb.append("-s "); //$NON-NLS-1$
+
+ if (mTraceDuration > 0) {
+ sb.append("-t"); //$NON-NLS-1$
+ sb.append(mTraceDuration);
+ sb.append(' ');
+ }
+
+ if (mTraceBufferSize > 0) {
+ sb.append("-b "); //$NON-NLS-1$
+ sb.append(mTraceBufferSize);
+ sb.append(' ');
+ }
+
+ return sb.toString().trim();
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceOptionsDialogV2.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceOptionsDialogV2.java
new file mode 100644
index 000000000..e28edd389
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceOptionsDialogV2.java
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2012 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.ide.eclipse.ddms.systrace;
+
+import com.android.ddmuilib.TableHelper;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.Text;
+
+import java.io.File;
+import java.util.List;
+import java.util.Set;
+
+public class SystraceOptionsDialogV2 extends TitleAreaDialog implements ISystraceOptionsDialog {
+ private static final String TITLE = "Systrace (Android System Trace)";
+ private static final String DEFAULT_MESSAGE =
+ "Settings to use while capturing system level trace";
+ private static final String DEFAULT_TRACE_FNAME = "trace.html"; //$NON-NLS-1$
+ private static final Set<String> sCommonTags = ImmutableSet.of(
+ "am", "app", "dalvik", "disk", "gfx", "input", "res", "sched", "view", "webview", "wm");
+
+ private Text mDestinationText;
+ private String mDestinationPath;
+ private Text mTraceDurationText;
+ private Text mTraceBufferSizeText;
+ private Combo mTraceAppCombo;
+
+ private static String sSaveToFolder = System.getProperty("user.home"); //$NON-NLS-1$
+ private static String sTraceDuration = "5";
+ private static String sTraceBufferSize = "2048";
+ private static Set<String> sEnabledTags = Sets.newHashSet(sCommonTags);
+ private static String sLastSelectedApp = null;
+
+ private final List<SystraceTag> mCommonSupportedTags;
+ private final List<SystraceTag> mAdvancedSupportedTags;
+
+ private final List<String> mCurrentApps;
+
+ private final SystraceOptions mOptions = new SystraceOptions();
+ private Table mCommonTagsTable;
+ private Table mAdvancedTagsTable;
+
+ public SystraceOptionsDialogV2(Shell parentShell, List<SystraceTag> tags, List<String> apps) {
+ super(parentShell);
+ mCurrentApps = apps;
+
+ mCommonSupportedTags = Lists.newArrayListWithExpectedSize(tags.size());
+ mAdvancedSupportedTags = Lists.newArrayListWithExpectedSize(tags.size());
+
+ for (SystraceTag supportedTag : tags) {
+ if (sCommonTags.contains(supportedTag.tag)) {
+ mCommonSupportedTags.add(supportedTag);
+ } else {
+ mAdvancedSupportedTags.add(supportedTag);
+ }
+ }
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ setTitle(TITLE);
+ setMessage(DEFAULT_MESSAGE);
+
+ Composite c = new Composite(parent, SWT.BORDER);
+ c.setLayout(new GridLayout(3, false));
+ c.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ Label l = new Label(c, SWT.NONE);
+ l.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+ l.setText("Destination File: ");
+
+ mDestinationText = new Text(c, SWT.BORDER);
+ mDestinationText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+ mDestinationText.setText(sSaveToFolder + File.separator + DEFAULT_TRACE_FNAME);
+
+ final Button browse = new Button(c, SWT.NONE);
+ browse.setText("Browse...");
+ browse.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ String path = openBrowseDialog(browse.getShell());
+ if (path != null) mDestinationText.setText(path);
+ }
+ });
+
+ Label lblTraceDurationseconds = new Label(c, SWT.NONE);
+ lblTraceDurationseconds.setLayoutData(
+ new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+ lblTraceDurationseconds.setText("Trace duration (seconds): ");
+
+ mTraceDurationText = new Text(c, SWT.BORDER);
+ mTraceDurationText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
+ mTraceDurationText.setText(sTraceDuration);
+
+ Label lblTraceBufferSize = new Label(c, SWT.NONE);
+ lblTraceBufferSize.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+ lblTraceBufferSize.setText("Trace Buffer Size (kb): ");
+
+ mTraceBufferSizeText = new Text(c, SWT.BORDER);
+ mTraceBufferSizeText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
+ mTraceBufferSizeText.setText(sTraceBufferSize);
+
+ Label lblTraceAppName = new Label(c, SWT.NONE);
+ lblTraceAppName.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+ lblTraceAppName.setText("Enable Application Traces from: ");
+
+ mTraceAppCombo = new Combo(c, SWT.DROP_DOWN | SWT.READ_ONLY);
+ mTraceAppCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
+ String[] items = new String[mCurrentApps.size() + 1];
+ items[0] = "None";
+ for (int i = 0; i < mCurrentApps.size(); i++) {
+ items[i+1] = mCurrentApps.get(i);
+ }
+ mTraceAppCombo.setItems(items);
+ if (sLastSelectedApp != null) {
+ mTraceAppCombo.setText(sLastSelectedApp);
+ } else {
+ mTraceAppCombo.select(0);
+ }
+
+ Label separator = new Label(c, SWT.SEPARATOR | SWT.HORIZONTAL);
+ GridData gd = new GridData(GridData.FILL_HORIZONTAL);
+ gd.horizontalSpan = 3;
+ separator.setLayoutData(gd);
+
+ ModifyListener m = new ModifyListener() {
+ @Override
+ public void modifyText(ModifyEvent e) {
+ validateFields();
+ }
+ };
+
+ mDestinationText.addModifyListener(m);
+ mTraceBufferSizeText.addModifyListener(m);
+ mTraceDurationText.addModifyListener(m);
+
+ mCommonTagsTable = createTable(c, "Commonly Used Tags: ", mCommonSupportedTags);
+ mAdvancedTagsTable = createTable(c, "Advanced Options: ", mAdvancedSupportedTags);
+
+ return c;
+ }
+
+ private Table createTable(Composite c, String label, List<SystraceTag> supportedTags) {
+ Label l = new Label(c, SWT.NONE);
+ l.setText(label);
+ l.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+
+ Table table = new Table(c, SWT.CHECK | SWT.BORDER);
+ GridData gd = new GridData(GridData.FILL_BOTH);
+ gd.horizontalSpan = 2;
+ table.setLayoutData(gd);
+ table.setHeaderVisible(false);
+ table.setLinesVisible(false);
+
+ for (SystraceTag tag : supportedTags) {
+ TableItem item = new TableItem(table, SWT.NONE);
+ item.setText(tag.info);
+ item.setChecked(sEnabledTags.contains(tag.tag));
+ }
+
+ TableHelper.createTableColumn(table,
+ "TagHeaderNotDisplayed", //$NON-NLS-1$
+ SWT.LEFT,
+ "SampleTagForColumnLengthCalculation", //$NON-NLS-1$
+ null,
+ null);
+
+ return table;
+ }
+
+ private void validateFields() {
+ // validate trace destination path
+ String msg = validatePath(mDestinationText.getText());
+ if (msg != null) {
+ setErrorMessage(msg);
+ getButton(OK).setEnabled(false);
+ return;
+ }
+
+ // validate the trace duration
+ if (!validateInteger(mTraceDurationText.getText())) {
+ setErrorMessage("Trace Duration should be a valid integer (seconds)");
+ getButton(OK).setEnabled(false);
+ return;
+ }
+
+ // validate the trace buffer size
+ if (!validateInteger(mTraceBufferSizeText.getText())) {
+ setErrorMessage("Trace Buffer Size should be a valid integer (kilobytes)");
+ getButton(OK).setEnabled(false);
+ return;
+ }
+
+ getButton(OK).setEnabled(true);
+ setErrorMessage(null);
+ }
+
+ private boolean validateInteger(String text) {
+ if (text == null || text.isEmpty()) {
+ return true;
+ }
+
+ try {
+ Integer.parseInt(text);
+ return true;
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+
+ private String validatePath(String path) {
+ if (path == null || path.isEmpty()) {
+ return null;
+ }
+
+ File f = new File(path);
+ if (f.isDirectory()) {
+ return String.format("The path '%s' points to a folder", path);
+ }
+
+ if (!f.exists()) { // if such a file doesn't exist, make sure the parent folder is valid
+ if (!f.getParentFile().isDirectory()) {
+ return String.format("That path '%s' is not a valid folder.", f.getParent());
+ }
+ }
+
+ return null;
+ }
+
+ private String openBrowseDialog(Shell parentShell) {
+ FileDialog fd = new FileDialog(parentShell, SWT.SAVE);
+
+ fd.setText("Save To");
+ fd.setFileName(DEFAULT_TRACE_FNAME);
+
+ fd.setFilterPath(sSaveToFolder);
+ fd.setFilterExtensions(new String[] { "*.html" }); //$NON-NLS-1$
+
+ String fname = fd.open();
+ if (fname == null || fname.trim().length() == 0) {
+ return null;
+ }
+
+ sSaveToFolder = fd.getFilterPath();
+ return fname;
+ }
+
+ @Override
+ protected void okPressed() {
+ mDestinationPath = mDestinationText.getText().trim();
+
+ sTraceDuration = mTraceDurationText.getText();
+ if (!sTraceDuration.isEmpty()) {
+ mOptions.mTraceDuration = Integer.parseInt(sTraceDuration);
+ }
+
+ sTraceBufferSize = mTraceBufferSizeText.getText();
+ if (!sTraceBufferSize.isEmpty()) {
+ mOptions.mTraceBufferSize = Integer.parseInt(sTraceBufferSize);
+ }
+
+ if (mTraceAppCombo.getSelectionIndex() != 0) {
+ mOptions.mTraceApp = sLastSelectedApp = mTraceAppCombo.getText();
+ }
+
+ sEnabledTags.clear();
+ sEnabledTags.addAll(getEnabledTags(mCommonTagsTable, mCommonSupportedTags));
+ sEnabledTags.addAll(getEnabledTags(mAdvancedTagsTable, mAdvancedSupportedTags));
+
+ super.okPressed();
+ }
+
+ private static Set<String> getEnabledTags(Table table, List<SystraceTag> tags) {
+ Set<String> enabledTags = Sets.newHashSetWithExpectedSize(tags.size());
+
+ for (int i = 0; i < table.getItemCount(); i++) {
+ TableItem it = table.getItem(i);
+ if (it.getChecked()) {
+ enabledTags.add(tags.get(i).tag);
+ }
+ }
+
+ return enabledTags;
+ }
+
+ @Override
+ public ISystraceOptions getSystraceOptions() {
+ return mOptions;
+ }
+
+ @Override
+ public String getTraceFilePath() {
+ return mDestinationPath;
+ }
+
+ private class SystraceOptions implements ISystraceOptions {
+ private int mTraceBufferSize;
+ private int mTraceDuration;
+ private String mTraceApp;
+
+ @Override
+ public String getTags() {
+ return null;
+ }
+
+ @Override
+ public String getOptions() {
+ StringBuilder sb = new StringBuilder(5 * mCommonSupportedTags.size());
+
+ if (mTraceApp != null) {
+ sb.append("-a "); //$NON-NLS-1$
+ sb.append(mTraceApp);
+ sb.append(' ');
+ }
+
+ if (mTraceDuration > 0) {
+ sb.append("-t"); //$NON-NLS-1$
+ sb.append(mTraceDuration);
+ sb.append(' ');
+ }
+
+ if (mTraceBufferSize > 0) {
+ sb.append("-b "); //$NON-NLS-1$
+ sb.append(mTraceBufferSize);
+ sb.append(' ');
+ }
+
+ for (String s : sEnabledTags) {
+ sb.append(s);
+ sb.append(' ');
+ }
+
+ return sb.toString().trim();
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceOutputParser.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceOutputParser.java
new file mode 100644
index 000000000..2548edc23
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceOutputParser.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2012 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.ide.eclipse.ddms.systrace;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.Files;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.zip.DataFormatException;
+import java.util.zip.Inflater;
+
+/** {@link SystraceOutputParser} receives the output of atrace command run on the device,
+ * parses it and generates html based on the trace */
+public class SystraceOutputParser {
+ private static final String TRACE_START = "TRACE:\n"; //$NON-NLS-1$
+
+ private final boolean mUncompress;
+ private final String mJs;
+ private final String mCss;
+ private final String mHtmlPrefix;
+ private final String mHtmlSuffix;
+
+ private byte[] mAtraceOutput;
+ private int mAtraceLength;
+ private int mSystraceIndex = -1;
+
+ /**
+ * Constructs a atrace output parser.
+ * @param compressedStream Is the input stream compressed using zlib?
+ * @param systraceJs systrace javascript content
+ * @param systraceCss systrace css content
+ */
+ public SystraceOutputParser(boolean compressedStream, String systraceJs, String systraceCss,
+ String htmlPrefix, String htmlSuffix) {
+ mUncompress = compressedStream;
+ mJs = systraceJs;
+ mCss = systraceCss;
+ mHtmlPrefix = htmlPrefix;
+ mHtmlSuffix = htmlSuffix;
+ }
+
+ /**
+ * Parses the atrace output for systrace content.
+ * @param atraceOutput output bytes from atrace
+ */
+ public void parse(byte[] atraceOutput) {
+ mAtraceOutput = atraceOutput;
+ mAtraceLength = atraceOutput.length;
+
+ removeCrLf();
+
+ // locate the trace start marker within the first hundred bytes
+ String header = new String(mAtraceOutput, 0, Math.min(100, mAtraceLength));
+ mSystraceIndex = locateSystraceData(header);
+
+ if (mSystraceIndex < 0) {
+ throw new RuntimeException("Unable to find trace start marker 'TRACE:':\n" + header);
+ }
+ }
+
+ /** Replaces \r\n with \n in {@link #mAtraceOutput}. */
+ private void removeCrLf() {
+ int dst = 0;
+ for (int src = 0; src < mAtraceLength - 1; src++, dst++) {
+ byte copy;
+ if (mAtraceOutput[src] == '\r' && mAtraceOutput[src + 1] == '\n') {
+ copy = '\n';
+ src++;
+ } else {
+ copy = mAtraceOutput[src];
+ }
+ mAtraceOutput[dst] = copy;
+ }
+
+ mAtraceLength = dst;
+ }
+
+ private int locateSystraceData(String header) {
+ int index = header.indexOf(TRACE_START);
+ if (index < 0) {
+ return -1;
+ } else {
+ return index + TRACE_START.length();
+ }
+ }
+
+ public String getSystraceHtml() {
+ if (mSystraceIndex < 0) {
+ return "";
+ }
+
+ String trace = "";
+ if (mUncompress) {
+ Inflater decompressor = new Inflater();
+ decompressor.setInput(mAtraceOutput, mSystraceIndex, mAtraceLength - mSystraceIndex);
+
+ byte[] buf = new byte[4096];
+ int n;
+ StringBuilder sb = new StringBuilder(1000);
+ try {
+ while ((n = decompressor.inflate(buf)) > 0) {
+ sb.append(new String(buf, 0, n));
+ }
+ } catch (DataFormatException e) {
+ throw new RuntimeException(e);
+ }
+ decompressor.end();
+
+ trace = sb.toString();
+ } else {
+ trace = new String(mAtraceOutput, mSystraceIndex, mAtraceLength - mSystraceIndex);
+ }
+
+ // each line should end with the characters \n\ followed by a newline
+ String html_out = trace.replaceAll("\n", "\\\\n\\\\\n");
+ String header = String.format(mHtmlPrefix, mCss, mJs, "");
+ String footer = mHtmlSuffix;
+ return header + html_out + footer;
+ }
+
+ public static String getJs(File assetsFolder) {
+ try {
+ return String.format("<script language=\"javascript\">%s</script>",
+ Files.toString(new File(assetsFolder, "script.js"), Charsets.UTF_8));
+ } catch (IOException e) {
+ return "";
+ }
+ }
+
+ public static String getCss(File assetsFolder) {
+ try {
+ return String.format("<style type=\"text/css\">%s</style>",
+ Files.toString(new File(assetsFolder, "style.css"), Charsets.UTF_8));
+ } catch (IOException e) {
+ return "";
+ }
+ }
+
+ public static String getHtmlPrefix(File assetsFolder) {
+ return getHtmlTemplate(assetsFolder, "prefix.html");
+ }
+
+ public static String getHtmlSuffix(File assetsFolder) {
+ return getHtmlTemplate(assetsFolder, "suffix.html");
+ }
+
+ private static String getHtmlTemplate(File assetsFolder, String htmlFileName) {
+ try {
+ return Files.toString(new File(assetsFolder, htmlFileName), Charsets.UTF_8);
+ } catch (IOException e) {
+ return "";
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceTag.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceTag.java
new file mode 100644
index 000000000..0fc03efa3
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceTag.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2012 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.ide.eclipse.ddms.systrace;
+
+public class SystraceTag {
+ public final String tag;
+ public final String info;
+
+ public SystraceTag(String tagName, String details) {
+ tag = tagName;
+ info = details;
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceTask.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceTask.java
new file mode 100644
index 000000000..726296468
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceTask.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2012 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.ide.eclipse.ddms.systrace;
+
+import com.android.ddmlib.IDevice;
+import com.android.ddmlib.IShellOutputReceiver;
+import com.google.common.primitives.Bytes;
+
+public class SystraceTask implements Runnable {
+ private final IDevice mDevice;
+ private final String mOptions;
+
+ private volatile boolean mCancel;
+
+ private final Object mLock = new Object();
+ private String errorMessage;
+ private boolean mTraceComplete;
+ private byte[] mBuffer = new byte[1024];
+ private int mDataLength = 0;
+
+ public SystraceTask(IDevice device, String options) {
+ mDevice = device;
+ mOptions = options;
+ }
+
+ @Override
+ public void run() {
+ try {
+ mDevice.executeShellCommand("atrace " + mOptions, new Receiver(), 0);
+ } catch (Exception e) {
+ synchronized (mLock) {
+ errorMessage = "Unexpected error while running atrace on device: " + e;
+ }
+ }
+ }
+
+ public void cancel() {
+ mCancel = true;
+ }
+
+ public String getError() {
+ synchronized (mLock) {
+ return errorMessage;
+ }
+ }
+
+ public byte[] getAtraceOutput() {
+ synchronized (mLock) {
+ return mTraceComplete ? mBuffer : null;
+ }
+ }
+
+ private class Receiver implements IShellOutputReceiver {
+ @Override
+ public void addOutput(byte[] data, int offset, int length) {
+ synchronized (mLock) {
+ if (mDataLength + length > mBuffer.length) {
+ mBuffer = Bytes.ensureCapacity(mBuffer, mDataLength + length + 1, 1024);
+ }
+
+ for (int i = 0; i < length; i++) {
+ mBuffer[mDataLength + i] = data[offset + i];
+ }
+ mDataLength += length;
+ }
+ }
+
+ @Override
+ public void flush() {
+ synchronized (mLock) {
+ // trim mBuffer to its final size
+ byte[] copy = new byte[mDataLength];
+ for (int i = 0; i < mDataLength; i++) {
+ copy[i] = mBuffer[i];
+ }
+ mBuffer = copy;
+
+ mTraceComplete = true;
+ }
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return mCancel;
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceVersionDetector.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceVersionDetector.java
new file mode 100644
index 000000000..646a45412
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceVersionDetector.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2012 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.ide.eclipse.ddms.systrace;
+
+import com.android.ddmlib.CollectingOutputReceiver;
+import com.android.ddmlib.IDevice;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class SystraceVersionDetector implements IRunnableWithProgress {
+ public static final int SYSTRACE_V1 = 1;
+ public static final int SYSTRACE_V2 = 2;
+
+ private final IDevice mDevice;
+ private List<SystraceTag> mTags;
+
+ public SystraceVersionDetector(IDevice device) {
+ mDevice = device;
+ }
+
+ @Override
+ public void run(IProgressMonitor monitor) throws InvocationTargetException,
+ InterruptedException {
+ monitor.beginTask("Checking systrace version on device..",
+ IProgressMonitor.UNKNOWN);
+
+ CountDownLatch setTagLatch = new CountDownLatch(1);
+ CollectingOutputReceiver receiver = new CollectingOutputReceiver(setTagLatch);
+ try {
+ String cmd = "atrace --list_categories";
+ mDevice.executeShellCommand(cmd, receiver);
+ setTagLatch.await(5, TimeUnit.SECONDS);
+ } catch (Exception e) {
+ throw new InvocationTargetException(e);
+ }
+
+ String shellOutput = receiver.getOutput();
+ mTags = parseSupportedTags(shellOutput);
+
+ monitor.done();
+ }
+
+ public int getVersion() {
+ if (mTags == null) {
+ return SYSTRACE_V1;
+ } else {
+ return SYSTRACE_V2;
+ }
+ }
+
+ public List<SystraceTag> getTags() {
+ return mTags;
+ }
+
+ private List<SystraceTag> parseSupportedTags(String listCategoriesOutput) {
+ if (listCategoriesOutput == null) {
+ return null;
+ }
+
+ if (listCategoriesOutput.contains("unknown option")) {
+ return null;
+ }
+
+ String[] categories = listCategoriesOutput.split("\n");
+ List<SystraceTag> tags = new ArrayList<SystraceTag>(categories.length);
+
+ Pattern p = Pattern.compile("([^-]+) - (.*)"); //$NON-NLS-1$
+ for (String category : categories) {
+ Matcher m = p.matcher(category);
+ if (m.find()) {
+ tags.add(new SystraceTag(m.group(1).trim(), m.group(2).trim()));
+ }
+ }
+
+ return tags;
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/AllocTrackerView.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/AllocTrackerView.java
new file mode 100644
index 000000000..e8b73ff51
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/AllocTrackerView.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2007 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.ide.eclipse.ddms.views;
+
+import com.android.ddmuilib.AllocationPanel;
+
+import org.eclipse.swt.widgets.Composite;
+
+public class AllocTrackerView extends TableView {
+
+ public static final String ID = "com.android.ide.eclipse.ddms.views.AllocTrackerView"; //$NON-NLS-1$
+ private AllocationPanel mPanel;
+
+ public AllocTrackerView() {
+ }
+
+ @Override
+ public void createPartControl(Composite parent) {
+ mPanel = new AllocationPanel();
+ mPanel.createPanel(parent);
+
+ setSelectionDependentPanel(mPanel);
+
+ // listen to focus changes for table(s) of the panel.
+ setupTableFocusListener(mPanel, parent);
+ }
+
+ @Override
+ public void setFocus() {
+ mPanel.setFocus();
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/DeviceView.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/DeviceView.java
new file mode 100644
index 000000000..c70c38803
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/DeviceView.java
@@ -0,0 +1,877 @@
+/*
+ * Copyright (C) 2008 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.ide.eclipse.ddms.views;
+
+import com.android.ddmlib.AndroidDebugBridge;
+import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
+import com.android.ddmlib.Client;
+import com.android.ddmlib.ClientData;
+import com.android.ddmlib.ClientData.IHprofDumpHandler;
+import com.android.ddmlib.ClientData.MethodProfilingStatus;
+import com.android.ddmlib.CollectingOutputReceiver;
+import com.android.ddmlib.DdmPreferences;
+import com.android.ddmlib.IDevice;
+import com.android.ddmlib.SyncException;
+import com.android.ddmlib.SyncService;
+import com.android.ddmlib.SyncService.ISyncProgressMonitor;
+import com.android.ddmlib.TimeoutException;
+import com.android.ddmuilib.DevicePanel;
+import com.android.ddmuilib.DevicePanel.IUiSelectionListener;
+import com.android.ddmuilib.ImageLoader;
+import com.android.ddmuilib.ScreenShotDialog;
+import com.android.ddmuilib.SyncProgressHelper;
+import com.android.ddmuilib.SyncProgressHelper.SyncRunnable;
+import com.android.ddmuilib.handler.BaseFileHandler;
+import com.android.ddmuilib.handler.MethodProfilingHandler;
+import com.android.ide.eclipse.ddms.DdmsPlugin;
+import com.android.ide.eclipse.ddms.IClientAction;
+import com.android.ide.eclipse.ddms.IDebuggerConnector;
+import com.android.ide.eclipse.ddms.editors.UiAutomatorViewer;
+import com.android.ide.eclipse.ddms.i18n.Messages;
+import com.android.ide.eclipse.ddms.preferences.PreferenceInitializer;
+import com.android.ide.eclipse.ddms.systrace.ISystraceOptions;
+import com.android.ide.eclipse.ddms.systrace.ISystraceOptionsDialog;
+import com.android.ide.eclipse.ddms.systrace.SystraceOptionsDialogV1;
+import com.android.ide.eclipse.ddms.systrace.SystraceOptionsDialogV2;
+import com.android.ide.eclipse.ddms.systrace.SystraceOutputParser;
+import com.android.ide.eclipse.ddms.systrace.SystraceTask;
+import com.android.ide.eclipse.ddms.systrace.SystraceVersionDetector;
+import com.android.uiautomator.UiAutomatorHelper;
+import com.android.uiautomator.UiAutomatorHelper.UiAutomatorException;
+import com.android.uiautomator.UiAutomatorHelper.UiAutomatorResult;
+import com.google.common.io.Files;
+
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.filesystem.IFileStore;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.ISharedImages;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.WorkbenchException;
+import org.eclipse.ui.ide.IDE;
+import org.eclipse.ui.part.ViewPart;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class DeviceView extends ViewPart implements IUiSelectionListener, IClientChangeListener {
+
+ private final static boolean USE_SELECTED_DEBUG_PORT = true;
+
+ public static final String ID = "com.android.ide.eclipse.ddms.views.DeviceView"; //$NON-NLS-1$
+
+ private static DeviceView sThis;
+
+ private Shell mParentShell;
+ private DevicePanel mDeviceList;
+
+ private Action mResetAdbAction;
+ private Action mCaptureAction;
+ private Action mViewUiAutomatorHierarchyAction;
+ private Action mSystraceAction;
+ private Action mUpdateThreadAction;
+ private Action mUpdateHeapAction;
+ private Action mGcAction;
+ private Action mKillAppAction;
+ private Action mDebugAction;
+ private Action mHprofAction;
+ private Action mTracingAction;
+
+ private ImageDescriptor mTracingStartImage;
+ private ImageDescriptor mTracingStopImage;
+
+ public class HProfHandler extends BaseFileHandler implements IHprofDumpHandler {
+ public final static String ACTION_SAVE = "hprof.save"; //$NON-NLS-1$
+ public final static String ACTION_OPEN = "hprof.open"; //$NON-NLS-1$
+
+ public final static String DOT_HPROF = ".hprof"; //$NON-NLS-1$
+
+ HProfHandler(Shell parentShell) {
+ super(parentShell);
+ }
+
+ @Override
+ protected String getDialogTitle() {
+ return Messages.DeviceView_HPROF_Error;
+ }
+
+ @Override
+ public void onEndFailure(final Client client, final String message) {
+ mParentShell.getDisplay().asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ displayErrorFromUiThread(
+ Messages.DeviceView_Unable_Create_HPROF_For_Application,
+ client.getClientData().getClientDescription(),
+ message != null ? message + "\n\n" : ""); //$NON-NLS-1$ //$NON-NLS-2$
+ } finally {
+ // this will make sure the dump hprof button is
+ // re-enabled for the
+ // current selection. as the client is finished dumping
+ // an hprof file
+ doSelectionChanged(mDeviceList.getSelectedClient());
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onSuccess(final String remoteFilePath, final Client client) {
+ mParentShell.getDisplay().asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ final IDevice device = client.getDevice();
+ try {
+ // get the sync service to pull the HPROF file
+ final SyncService sync = client.getDevice().getSyncService();
+ if (sync != null) {
+ // get from the preference what action to take
+ IPreferenceStore store = DdmsPlugin.getDefault().getPreferenceStore();
+ String value = store.getString(PreferenceInitializer.ATTR_HPROF_ACTION);
+
+ if (ACTION_OPEN.equals(value)) {
+ File temp = File.createTempFile("android", DOT_HPROF); //$NON-NLS-1$
+ final String tempPath = temp.getAbsolutePath();
+ SyncProgressHelper.run(new SyncRunnable() {
+
+ @Override
+ public void run(ISyncProgressMonitor monitor)
+ throws SyncException, IOException,
+ TimeoutException {
+ sync.pullFile(remoteFilePath, tempPath, monitor);
+ }
+
+ @Override
+ public void close() {
+ sync.close();
+ }
+ },
+ String.format(Messages.DeviceView_Pulling_From_Device,
+ remoteFilePath),
+ mParentShell);
+
+ open(tempPath);
+ } else {
+ // default action is ACTION_SAVE
+ promptAndPull(sync,
+ client.getClientData().getClientDescription() + DOT_HPROF,
+ remoteFilePath, Messages.DeviceView_Save_HPROF_File);
+
+ }
+ } else {
+ displayErrorFromUiThread(
+ Messages.DeviceView_Unable_Download_HPROF_From_Device_One_Param_First_Message,
+ device.getSerialNumber());
+ }
+ } catch (SyncException e) {
+ if (e.wasCanceled() == false) {
+ displayErrorFromUiThread(
+ Messages.DeviceView_Unable_Download_HPROF_From_Device_Two_Param,
+ device.getSerialNumber(), e.getMessage());
+ }
+ } catch (Exception e) {
+ displayErrorFromUiThread(
+ Messages.DeviceView_Unable_Download_HPROF_From_Device_One_Param_Second_Message,
+ device.getSerialNumber());
+
+ } finally {
+ // this will make sure the dump hprof button is
+ // re-enabled for the
+ // current selection. as the client is finished dumping
+ // an hprof file
+ doSelectionChanged(mDeviceList.getSelectedClient());
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onSuccess(final byte[] data, final Client client) {
+ mParentShell.getDisplay().asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ // get from the preference what action to take
+ IPreferenceStore store = DdmsPlugin.getDefault().getPreferenceStore();
+ String value = store.getString(PreferenceInitializer.ATTR_HPROF_ACTION);
+
+ if (ACTION_OPEN.equals(value)) {
+ try {
+ // no need to give an extension since we're going to
+ // convert the
+ // file anyway after.
+ File tempFile = saveTempFile(data, null /* extension */);
+ open(tempFile.getAbsolutePath());
+ } catch (Exception e) {
+ String errorMsg = e.getMessage();
+ displayErrorFromUiThread(
+ Messages.DeviceView_Failed_To_Save_HPROF_Data,
+ errorMsg != null ? ":\n" + errorMsg : "."); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ } else {
+ // default action is ACTION_SAVE
+ promptAndSave(client.getClientData().getClientDescription() + DOT_HPROF,
+ data, Messages.DeviceView_Save_HPROF_File);
+ }
+ }
+ });
+ }
+
+ private void open(String path) throws IOException, InterruptedException, PartInitException {
+ // make a temp file to convert the hprof into something
+ // readable by normal tools
+ File temp = File.createTempFile("android", DOT_HPROF); //$NON-NLS-1$
+ String tempPath = temp.getAbsolutePath();
+
+ String[] command = new String[3];
+ command[0] = DdmsPlugin.getHprofConverter();
+ command[1] = path;
+ command[2] = tempPath;
+
+ Process p = Runtime.getRuntime().exec(command);
+ p.waitFor();
+
+ IFileStore fileStore = EFS.getLocalFileSystem().getStore(new Path(tempPath));
+ if (!fileStore.fetchInfo().isDirectory() && fileStore.fetchInfo().exists()) {
+ // before we open the file in an editor window, we make sure the
+ // current
+ // workbench page has an editor area (typically the ddms
+ // perspective doesn't).
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
+ IWorkbenchPage page = window.getActivePage();
+ if (page == null) {
+ return;
+ }
+
+ if (page.isEditorAreaVisible() == false) {
+ IAdaptable input;
+ input = page.getInput();
+ try {
+ workbench.showPerspective("org.eclipse.debug.ui.DebugPerspective", //$NON-NLS-1$
+ window, input);
+ } catch (WorkbenchException e) {
+ }
+ }
+
+ IDE.openEditorOnFileStore(page, fileStore);
+ }
+ }
+ }
+
+ public DeviceView() {
+ // the view is declared with allowMultiple="false" so we
+ // can safely do this.
+ sThis = this;
+ }
+
+ public static DeviceView getInstance() {
+ return sThis;
+ }
+
+ @Override
+ public void createPartControl(Composite parent) {
+ mParentShell = parent.getShell();
+
+ ImageLoader loader = ImageLoader.getDdmUiLibLoader();
+
+ mDeviceList = new DevicePanel(USE_SELECTED_DEBUG_PORT);
+ mDeviceList.createPanel(parent);
+ mDeviceList.addSelectionListener(this);
+
+ DdmsPlugin plugin = DdmsPlugin.getDefault();
+ mDeviceList.addSelectionListener(plugin);
+ plugin.setListeningState(true);
+
+ mCaptureAction = new Action(Messages.DeviceView_Screen_Capture) {
+ @Override
+ public void run() {
+ ScreenShotDialog dlg = new ScreenShotDialog(
+ DdmsPlugin.getDisplay().getActiveShell());
+ dlg.open(mDeviceList.getSelectedDevice());
+ }
+ };
+ mCaptureAction.setToolTipText(Messages.DeviceView_Screen_Capture_Tooltip);
+ mCaptureAction.setImageDescriptor(loader.loadDescriptor("capture.png")); //$NON-NLS-1$
+
+ mViewUiAutomatorHierarchyAction = new Action("Dump View Hierarchy for UI Automator") {
+ @Override
+ public void run() {
+ takeUiAutomatorSnapshot(mDeviceList.getSelectedDevice(),
+ DdmsPlugin.getDisplay().getActiveShell());
+ }
+ };
+ mViewUiAutomatorHierarchyAction.setToolTipText("Dump View Hierarchy for UI Automator");
+ mViewUiAutomatorHierarchyAction.setImageDescriptor(
+ DdmsPlugin.getImageDescriptor("icons/uiautomator.png")); //$NON-NLS-1$
+
+ mSystraceAction = new Action("Capture System Wide Trace") {
+ @Override
+ public void run() {
+ launchSystrace(mDeviceList.getSelectedDevice(),
+ DdmsPlugin.getDisplay().getActiveShell());
+ }
+ };
+ mSystraceAction.setToolTipText("Capture system wide trace using Android systrace");
+ mSystraceAction.setImageDescriptor(
+ DdmsPlugin.getImageDescriptor("icons/systrace.png")); //$NON-NLS-1$
+ mSystraceAction.setEnabled(true);
+
+ mResetAdbAction = new Action(Messages.DeviceView_Reset_ADB) {
+ @Override
+ public void run() {
+ AndroidDebugBridge bridge = AndroidDebugBridge.getBridge();
+ if (bridge != null) {
+ if (bridge.restart() == false) {
+ // get the current Display
+ final Display display = DdmsPlugin.getDisplay();
+
+ // dialog box only run in ui thread..
+ display.asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ Shell shell = display.getActiveShell();
+ MessageDialog.openError(shell, Messages.DeviceView_ADB_Error,
+ Messages.DeviceView_ADB_Failed_Restart);
+ }
+ });
+ }
+ }
+ }
+ };
+ mResetAdbAction.setToolTipText(Messages.DeviceView_Reset_ADB_Host_Deamon);
+ mResetAdbAction.setImageDescriptor(PlatformUI.getWorkbench()
+ .getSharedImages().getImageDescriptor(
+ ISharedImages.IMG_OBJS_WARN_TSK));
+
+ mKillAppAction = new Action() {
+ @Override
+ public void run() {
+ mDeviceList.killSelectedClient();
+ }
+ };
+
+ mKillAppAction.setText(Messages.DeviceView_Stop_Process);
+ mKillAppAction.setToolTipText(Messages.DeviceView_Stop_Process_Tooltip);
+ mKillAppAction.setImageDescriptor(loader.loadDescriptor(DevicePanel.ICON_HALT));
+
+ mGcAction = new Action() {
+ @Override
+ public void run() {
+ mDeviceList.forceGcOnSelectedClient();
+ }
+ };
+
+ mGcAction.setText(Messages.DeviceView_Cause_GC);
+ mGcAction.setToolTipText(Messages.DeviceView_Cause_GC_Tooltip);
+ mGcAction.setImageDescriptor(loader.loadDescriptor(DevicePanel.ICON_GC));
+
+ mHprofAction = new Action() {
+ @Override
+ public void run() {
+ mDeviceList.dumpHprof();
+ doSelectionChanged(mDeviceList.getSelectedClient());
+ }
+ };
+ mHprofAction.setText(Messages.DeviceView_Dump_HPROF_File);
+ mHprofAction.setToolTipText(Messages.DeviceView_Dump_HPROF_File_Tooltip);
+ mHprofAction.setImageDescriptor(loader.loadDescriptor(DevicePanel.ICON_HPROF));
+
+ mUpdateHeapAction = new Action(Messages.DeviceView_Update_Heap, IAction.AS_CHECK_BOX) {
+ @Override
+ public void run() {
+ boolean enable = mUpdateHeapAction.isChecked();
+ mDeviceList.setEnabledHeapOnSelectedClient(enable);
+ }
+ };
+ mUpdateHeapAction.setToolTipText(Messages.DeviceView_Update_Heap_Tooltip);
+ mUpdateHeapAction.setImageDescriptor(loader.loadDescriptor(DevicePanel.ICON_HEAP));
+
+ mUpdateThreadAction = new Action(Messages.DeviceView_Threads, IAction.AS_CHECK_BOX) {
+ @Override
+ public void run() {
+ boolean enable = mUpdateThreadAction.isChecked();
+ mDeviceList.setEnabledThreadOnSelectedClient(enable);
+ }
+ };
+ mUpdateThreadAction.setToolTipText(Messages.DeviceView_Threads_Tooltip);
+ mUpdateThreadAction.setImageDescriptor(loader.loadDescriptor(DevicePanel.ICON_THREAD));
+
+ mTracingAction = new Action() {
+ @Override
+ public void run() {
+ mDeviceList.toggleMethodProfiling();
+ }
+ };
+ mTracingAction.setText(Messages.DeviceView_Start_Method_Profiling);
+ mTracingAction.setToolTipText(Messages.DeviceView_Start_Method_Profiling_Tooltip);
+ mTracingStartImage = loader.loadDescriptor(DevicePanel.ICON_TRACING_START);
+ mTracingStopImage = loader.loadDescriptor(DevicePanel.ICON_TRACING_STOP);
+ mTracingAction.setImageDescriptor(mTracingStartImage);
+
+ mDebugAction = new Action(Messages.DeviceView_Debug_Process) {
+ @Override
+ public void run() {
+ if (DdmsPlugin.getDefault().hasDebuggerConnectors()) {
+ Client currentClient = mDeviceList.getSelectedClient();
+ if (currentClient != null) {
+ ClientData clientData = currentClient.getClientData();
+
+ // make sure the client can be debugged
+ switch (clientData.getDebuggerConnectionStatus()) {
+ case ERROR: {
+ Display display = DdmsPlugin.getDisplay();
+ Shell shell = display.getActiveShell();
+ MessageDialog.openError(shell,
+ Messages.DeviceView_Debug_Process_Title,
+ Messages.DeviceView_Process_Debug_Already_In_Use);
+ return;
+ }
+ case ATTACHED: {
+ Display display = DdmsPlugin.getDisplay();
+ Shell shell = display.getActiveShell();
+ MessageDialog.openError(shell,
+ Messages.DeviceView_Debug_Process_Title,
+ Messages.DeviceView_Process_Already_Being_Debugged);
+ return;
+ }
+ }
+
+ // get the name of the client
+ String packageName = clientData.getClientDescription();
+ if (packageName != null) {
+
+ // try all connectors till one returns true.
+ IDebuggerConnector[] connectors =
+ DdmsPlugin.getDefault().getDebuggerConnectors();
+
+ if (connectors != null) {
+ for (IDebuggerConnector connector : connectors) {
+ try {
+ if (connector.connectDebugger(packageName,
+ currentClient.getDebuggerListenPort(),
+ DdmPreferences.getSelectedDebugPort())) {
+ return;
+ }
+ } catch (Throwable t) {
+ // ignore, we'll just not use this
+ // implementation
+ }
+ }
+ }
+
+ // if we get to this point, then we failed to find a
+ // project
+ // that matched the application to debug
+ Display display = DdmsPlugin.getDisplay();
+ Shell shell = display.getActiveShell();
+ MessageDialog.openError(shell, Messages.DeviceView_Debug_Process_Title,
+ String.format(
+ Messages.DeviceView_Debug_Session_Failed,
+ packageName));
+ }
+ }
+ }
+ }
+ };
+ mDebugAction.setToolTipText(Messages.DeviceView_Debug_Process_Tooltip);
+ mDebugAction.setImageDescriptor(loader.loadDescriptor("debug-attach.png")); //$NON-NLS-1$
+ mDebugAction.setEnabled(DdmsPlugin.getDefault().hasDebuggerConnectors());
+
+ placeActions();
+
+ // disabling all action buttons
+ selectionChanged(null, null);
+
+ ClientData.setHprofDumpHandler(new HProfHandler(mParentShell));
+ AndroidDebugBridge.addClientChangeListener(this);
+ ClientData.setMethodProfilingHandler(new MethodProfilingHandler(mParentShell) {
+ @Override
+ protected void open(String tempPath) {
+ if (DdmsPlugin.getDefault().launchTraceview(tempPath) == false) {
+ super.open(tempPath);
+ }
+ }
+ });
+ }
+
+ private void takeUiAutomatorSnapshot(final IDevice device, final Shell shell) {
+ ProgressMonitorDialog dialog = new ProgressMonitorDialog(shell);
+ try {
+ dialog.run(true, false, new IRunnableWithProgress() {
+ @Override
+ public void run(IProgressMonitor monitor) throws InvocationTargetException,
+ InterruptedException {
+ UiAutomatorResult result = null;
+ try {
+ result = UiAutomatorHelper.takeSnapshot(device, monitor);
+ } catch (UiAutomatorException e) {
+ throw new InvocationTargetException(e);
+ } finally {
+ monitor.done();
+ }
+
+ UiAutomatorViewer.openEditor(result);
+ }
+ });
+ } catch (Exception e) {
+ Throwable t = e;
+ if (e instanceof InvocationTargetException) {
+ t = ((InvocationTargetException) e).getTargetException();
+ }
+ Status s = new Status(IStatus.ERROR, DdmsPlugin.PLUGIN_ID,
+ "Error obtaining UI hierarchy", t);
+ ErrorDialog.openError(shell, "UI Automator",
+ "Unexpected error while obtaining UI hierarchy", s);
+ }
+ };
+
+ private void launchSystrace(final IDevice device, final Shell parentShell) {
+ final File systraceAssets = new File(DdmsPlugin.getPlatformToolsFolder(), "systrace"); //$NON-NLS-1$
+ if (!systraceAssets.isDirectory()) {
+ MessageDialog.openError(parentShell, "Systrace",
+ "Updated version of platform-tools (18.0.1 or greater) is required.\n"
+ + "Please update your platform-tools using SDK Manager.");
+ return;
+ }
+
+ SystraceVersionDetector detector = new SystraceVersionDetector(device);
+ try {
+ new ProgressMonitorDialog(parentShell).run(true, false, detector);
+ } catch (InvocationTargetException e) {
+ MessageDialog.openError(parentShell,
+ "Systrace",
+ "Unexpected error while detecting atrace version: " + e);
+ return;
+ } catch (InterruptedException e) {
+ return;
+ }
+
+ final ISystraceOptionsDialog dlg;
+ if (detector.getVersion() == SystraceVersionDetector.SYSTRACE_V1) {
+ dlg = new SystraceOptionsDialogV1(parentShell);
+ } else {
+ Client[] clients = device.getClients();
+ List<String> apps = new ArrayList<String>(clients.length);
+ for (int i = 0; i < clients.length; i++) {
+ String name = clients[i].getClientData().getClientDescription();
+ if (name != null && !name.isEmpty()) {
+ apps.add(name);
+ }
+ }
+ dlg = new SystraceOptionsDialogV2(parentShell, detector.getTags(), apps);
+ }
+
+ if (dlg.open() != SystraceOptionsDialogV1.OK) {
+ return;
+ }
+
+ final ISystraceOptions options = dlg.getSystraceOptions();
+
+ // set trace tag if necessary:
+ // adb shell setprop debug.atrace.tags.enableflags <tag>
+ String tag = options.getTags();
+ if (tag != null) {
+ CountDownLatch setTagLatch = new CountDownLatch(1);
+ CollectingOutputReceiver receiver = new CollectingOutputReceiver(setTagLatch);
+ try {
+ String cmd = "setprop debug.atrace.tags.enableflags " + tag;
+ device.executeShellCommand(cmd, receiver);
+ setTagLatch.await(5, TimeUnit.SECONDS);
+ } catch (Exception e) {
+ MessageDialog.openError(parentShell,
+ "Systrace",
+ "Unexpected error while setting trace tags: " + e);
+ return;
+ }
+
+ String shellOutput = receiver.getOutput();
+ if (shellOutput.contains("Error type")) { //$NON-NLS-1$
+ throw new RuntimeException(receiver.getOutput());
+ }
+ }
+
+ // obtain the output of "adb shell atrace <trace-options>" and generate the html file
+ ProgressMonitorDialog d = new ProgressMonitorDialog(parentShell);
+ try {
+ d.run(true, true, new IRunnableWithProgress() {
+ @Override
+ public void run(IProgressMonitor monitor) throws InvocationTargetException,
+ InterruptedException {
+ boolean COMPRESS_DATA = true;
+
+ monitor.setTaskName("Collecting Trace Information");
+ final String atraceOptions = options.getOptions()
+ + (COMPRESS_DATA ? " -z" : "");
+ SystraceTask task = new SystraceTask(device, atraceOptions);
+ Thread t = new Thread(task, "Systrace Output Receiver");
+ t.start();
+
+ // check if the user has cancelled tracing every so often
+ while (true) {
+ t.join(1000);
+
+ if (t.isAlive()) {
+ if (monitor.isCanceled()) {
+ task.cancel();
+ return;
+ }
+ } else {
+ break;
+ }
+ }
+
+ if (task.getError() != null) {
+ throw new RuntimeException(task.getError());
+ }
+
+ monitor.setTaskName("Saving trace information");
+ SystraceOutputParser parser = new SystraceOutputParser(
+ COMPRESS_DATA,
+ SystraceOutputParser.getJs(systraceAssets),
+ SystraceOutputParser.getCss(systraceAssets),
+ SystraceOutputParser.getHtmlPrefix(systraceAssets),
+ SystraceOutputParser.getHtmlSuffix(systraceAssets));
+
+ parser.parse(task.getAtraceOutput());
+
+ String html = parser.getSystraceHtml();
+ try {
+ Files.write(html.getBytes(), new File(dlg.getTraceFilePath()));
+ } catch (IOException e) {
+ throw new InvocationTargetException(e);
+ }
+ }
+ });
+ } catch (InvocationTargetException e) {
+ ErrorDialog.openError(parentShell, "Systrace",
+ "Unable to collect system trace.",
+ new Status(Status.ERROR,
+ DdmsPlugin.PLUGIN_ID,
+ "Unexpected error while collecting system trace.",
+ e.getCause()));
+ } catch (InterruptedException ignore) {
+ }
+ }
+
+ @Override
+ public void setFocus() {
+ mDeviceList.setFocus();
+ }
+
+ /**
+ * Sent when a new {@link IDevice} and {@link Client} are selected.
+ *
+ * @param selectedDevice the selected device. If null, no devices are
+ * selected.
+ * @param selectedClient The selected client. If null, no clients are
+ * selected.
+ */
+ @Override
+ public void selectionChanged(IDevice selectedDevice, Client selectedClient) {
+ // update the buttons
+ doSelectionChanged(selectedClient);
+ doSelectionChanged(selectedDevice);
+ }
+
+ private void doSelectionChanged(Client selectedClient) {
+ // update the buttons
+ if (selectedClient != null) {
+ if (USE_SELECTED_DEBUG_PORT) {
+ // set the client as the debug client
+ selectedClient.setAsSelectedClient();
+ }
+
+ mDebugAction.setEnabled(DdmsPlugin.getDefault().hasDebuggerConnectors());
+ mKillAppAction.setEnabled(true);
+ mGcAction.setEnabled(true);
+
+ mUpdateHeapAction.setEnabled(true);
+ mUpdateHeapAction.setChecked(selectedClient.isHeapUpdateEnabled());
+
+ mUpdateThreadAction.setEnabled(true);
+ mUpdateThreadAction.setChecked(selectedClient.isThreadUpdateEnabled());
+
+ ClientData data = selectedClient.getClientData();
+
+ if (data.hasFeature(ClientData.FEATURE_HPROF)) {
+ mHprofAction.setEnabled(data.hasPendingHprofDump() == false);
+ mHprofAction.setToolTipText(Messages.DeviceView_Dump_HPROF_File);
+ } else {
+ mHprofAction.setEnabled(false);
+ mHprofAction
+ .setToolTipText(Messages.DeviceView_Dump_HPROF_File_Not_Supported_By_VM);
+ }
+
+ if (data.hasFeature(ClientData.FEATURE_PROFILING)) {
+ mTracingAction.setEnabled(true);
+ if (data.getMethodProfilingStatus() == MethodProfilingStatus.TRACER_ON
+ || data.getMethodProfilingStatus() == MethodProfilingStatus.SAMPLER_ON) {
+ mTracingAction
+ .setToolTipText(Messages.DeviceView_Stop_Method_Profiling_Tooltip);
+ mTracingAction.setText(Messages.DeviceView_Stop_Method_Profiling);
+ mTracingAction.setImageDescriptor(mTracingStopImage);
+ } else {
+ mTracingAction
+ .setToolTipText(Messages.DeviceView_Start_Method_Profiling_Tooltip);
+ mTracingAction.setImageDescriptor(mTracingStartImage);
+ mTracingAction.setText(Messages.DeviceView_Start_Method_Profiling);
+ }
+ } else {
+ mTracingAction.setEnabled(false);
+ mTracingAction.setImageDescriptor(mTracingStartImage);
+ mTracingAction
+ .setToolTipText(Messages.DeviceView_Start_Method_Profiling_Not_Suported_By_Vm);
+ mTracingAction.setText(Messages.DeviceView_Start_Method_Profiling);
+ }
+ } else {
+ if (USE_SELECTED_DEBUG_PORT) {
+ // set the client as the debug client
+ AndroidDebugBridge bridge = AndroidDebugBridge.getBridge();
+ if (bridge != null) {
+ bridge.setSelectedClient(null);
+ }
+ }
+
+ mDebugAction.setEnabled(false);
+ mKillAppAction.setEnabled(false);
+ mGcAction.setEnabled(false);
+ mUpdateHeapAction.setChecked(false);
+ mUpdateHeapAction.setEnabled(false);
+ mUpdateThreadAction.setEnabled(false);
+ mUpdateThreadAction.setChecked(false);
+ mHprofAction.setEnabled(false);
+
+ mHprofAction.setEnabled(false);
+ mHprofAction.setToolTipText(Messages.DeviceView_Dump_HPROF_File);
+
+ mTracingAction.setEnabled(false);
+ mTracingAction.setImageDescriptor(mTracingStartImage);
+ mTracingAction.setToolTipText(Messages.DeviceView_Start_Method_Profiling_Tooltip);
+ mTracingAction.setText(Messages.DeviceView_Start_Method_Profiling);
+ }
+
+ for (IClientAction a : DdmsPlugin.getDefault().getClientSpecificActions()) {
+ a.selectedClientChanged(selectedClient);
+ }
+ }
+
+ private void doSelectionChanged(IDevice selectedDevice) {
+ boolean validDevice = selectedDevice != null;
+
+ mCaptureAction.setEnabled(validDevice);
+ mViewUiAutomatorHierarchyAction.setEnabled(validDevice);
+ mSystraceAction.setEnabled(validDevice);
+ }
+
+ /**
+ * Place the actions in the ui.
+ */
+ private final void placeActions() {
+ IActionBars actionBars = getViewSite().getActionBars();
+
+ // first in the menu
+ IMenuManager menuManager = actionBars.getMenuManager();
+ menuManager.removeAll();
+ menuManager.add(mDebugAction);
+ menuManager.add(new Separator());
+ menuManager.add(mUpdateHeapAction);
+ menuManager.add(mHprofAction);
+ menuManager.add(mGcAction);
+ menuManager.add(new Separator());
+ menuManager.add(mUpdateThreadAction);
+ menuManager.add(mTracingAction);
+ menuManager.add(new Separator());
+ menuManager.add(mKillAppAction);
+ menuManager.add(new Separator());
+ menuManager.add(mCaptureAction);
+ menuManager.add(new Separator());
+ menuManager.add(mViewUiAutomatorHierarchyAction);
+ menuManager.add(new Separator());
+ menuManager.add(mSystraceAction);
+ menuManager.add(new Separator());
+ menuManager.add(mResetAdbAction);
+ for (IClientAction a : DdmsPlugin.getDefault().getClientSpecificActions()) {
+ menuManager.add(a.getAction());
+ }
+
+ // and then in the toolbar
+ IToolBarManager toolBarManager = actionBars.getToolBarManager();
+ toolBarManager.removeAll();
+ toolBarManager.add(mDebugAction);
+ toolBarManager.add(new Separator());
+ toolBarManager.add(mUpdateHeapAction);
+ toolBarManager.add(mHprofAction);
+ toolBarManager.add(mGcAction);
+ toolBarManager.add(new Separator());
+ toolBarManager.add(mUpdateThreadAction);
+ toolBarManager.add(mTracingAction);
+ toolBarManager.add(new Separator());
+ toolBarManager.add(mKillAppAction);
+ toolBarManager.add(new Separator());
+ toolBarManager.add(mCaptureAction);
+ toolBarManager.add(new Separator());
+ toolBarManager.add(mViewUiAutomatorHierarchyAction);
+ toolBarManager.add(new Separator());
+ toolBarManager.add(mSystraceAction);
+ for (IClientAction a : DdmsPlugin.getDefault().getClientSpecificActions()) {
+ toolBarManager.add(a.getAction());
+ }
+ }
+
+ @Override
+ public void clientChanged(final Client client, int changeMask) {
+ if ((changeMask & Client.CHANGE_METHOD_PROFILING_STATUS) == Client.CHANGE_METHOD_PROFILING_STATUS) {
+ if (mDeviceList.getSelectedClient() == client) {
+ mParentShell.getDisplay().asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ // force refresh of the button enabled state.
+ doSelectionChanged(client);
+ }
+ });
+ }
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/EmulatorControlView.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/EmulatorControlView.java
new file mode 100644
index 000000000..0dda7ef61
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/EmulatorControlView.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2007 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.ide.eclipse.ddms.views;
+
+import com.android.ddmuilib.EmulatorControlPanel;
+
+import org.eclipse.swt.widgets.Composite;
+
+public class EmulatorControlView extends SelectionDependentViewPart {
+
+ public static final String ID =
+ "com.android.ide.eclipse.ddms.views.EmulatorControlView"; //$NON-NLS-1$
+
+ private EmulatorControlPanel mPanel;
+
+ @Override
+ public void createPartControl(Composite parent) {
+ mPanel = new EmulatorControlPanel();
+ mPanel.createPanel(parent);
+ setSelectionDependentPanel(mPanel);
+ }
+
+ @Override
+ public void setFocus() {
+ mPanel.setFocus();
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/EventLogView.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/EventLogView.java
new file mode 100644
index 000000000..653f17f43
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/EventLogView.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2008 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.ide.eclipse.ddms.views;
+
+import com.android.ddmuilib.ImageLoader;
+import com.android.ddmuilib.log.event.EventLogPanel;
+import com.android.ide.eclipse.ddms.CommonAction;
+import com.android.ide.eclipse.ddms.i18n.Messages;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.IActionBars;
+
+public class EventLogView extends SelectionDependentViewPart {
+
+ private EventLogPanel mLogPanel;
+
+ @Override
+ public void createPartControl(Composite parent) {
+ ImageLoader loader = ImageLoader.getDdmUiLibLoader();
+
+ // create the external actions
+ CommonAction optionsAction = new CommonAction(Messages.EventLogView_Options);
+ optionsAction.setToolTipText(Messages.EventLogView_Opens_Options_Panel);
+ optionsAction.setImageDescriptor(loader.loadDescriptor("edit.png")); //$NON-NLS-1$
+
+ CommonAction clearLogAction = new CommonAction(Messages.EventLogView_Clear_Log);
+ clearLogAction.setToolTipText(Messages.EventLogView_Clears_Event_Log);
+ clearLogAction.setImageDescriptor(loader.loadDescriptor("clear.png")); //$NON-NLS-1$
+
+ CommonAction saveAction = new CommonAction(Messages.EventLogView_Save_Log);
+ saveAction.setToolTipText(Messages.EventLogView_Saves_Event_Log);
+ saveAction.setImageDescriptor(loader.loadDescriptor("save.png")); //$NON-NLS-1$
+
+ CommonAction loadAction = new CommonAction(Messages.EventLogView_Load_Log);
+ loadAction.setToolTipText(Messages.EventLogView_Loads_Event_Log);
+ loadAction.setImageDescriptor(loader.loadDescriptor("load.png")); //$NON-NLS-1$
+
+ CommonAction importBugAction = new CommonAction(Messages.EventLogView_Import_Bug_Report_Log);
+ importBugAction.setToolTipText(Messages.EventLogView_Imports_Bug_Report);
+ importBugAction.setImageDescriptor(loader.loadDescriptor("importBug.png")); //$NON-NLS-1$
+
+ placeActions(optionsAction, clearLogAction, saveAction, loadAction, importBugAction);
+
+ mLogPanel = new EventLogPanel();
+ mLogPanel
+ .setActions(optionsAction, clearLogAction, saveAction, loadAction, importBugAction);
+ mLogPanel.createPanel(parent);
+ setSelectionDependentPanel(mLogPanel);
+ }
+
+ @Override
+ public void setFocus() {
+ mLogPanel.setFocus();
+ }
+
+ @Override
+ public void dispose() {
+ if (mLogPanel != null) {
+ mLogPanel.stopEventLog(true);
+ }
+ }
+
+ /**
+ * Places the actions in the toolbar and in the menu.
+ *
+ * @param importBugAction
+ */
+ private void placeActions(IAction optionAction, IAction clearAction, IAction saveAction,
+ IAction loadAction, CommonAction importBugAction) {
+ IActionBars actionBars = getViewSite().getActionBars();
+
+ // first in the menu
+ IMenuManager menuManager = actionBars.getMenuManager();
+ menuManager.add(clearAction);
+ menuManager.add(new Separator());
+ menuManager.add(saveAction);
+ menuManager.add(loadAction);
+ menuManager.add(importBugAction);
+ menuManager.add(new Separator());
+ menuManager.add(optionAction);
+
+ // and then in the toolbar
+ IToolBarManager toolBarManager = actionBars.getToolBarManager();
+ toolBarManager.add(clearAction);
+ toolBarManager.add(new Separator());
+ toolBarManager.add(saveAction);
+ toolBarManager.add(loadAction);
+ toolBarManager.add(importBugAction);
+ toolBarManager.add(new Separator());
+ toolBarManager.add(optionAction);
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/FileExplorerView.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/FileExplorerView.java
new file mode 100644
index 000000000..41796dcfd
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/FileExplorerView.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2007 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.ide.eclipse.ddms.views;
+
+import com.android.ddmlib.Client;
+import com.android.ddmlib.IDevice;
+import com.android.ddmuilib.ImageLoader;
+import com.android.ddmuilib.explorer.DeviceExplorer;
+import com.android.ide.eclipse.ddms.CommonAction;
+import com.android.ide.eclipse.ddms.DdmsPlugin;
+import com.android.ide.eclipse.ddms.DdmsPlugin.ISelectionListener;
+import com.android.ide.eclipse.ddms.i18n.Messages;
+
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.swt.graphics.Device;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.ISharedImages;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.part.ViewPart;
+
+public class FileExplorerView extends ViewPart implements ISelectionListener {
+
+ public static final String ID = "com.android.ide.eclipse.ddms.views.FileExplorerView"; //$NON-NLS-1$
+
+ private final static String COLUMN_NAME =
+ DdmsPlugin.PLUGIN_ID + ".explorer.name"; //$NON-NLS-1S
+ private final static String COLUMN_SIZE =
+ DdmsPlugin.PLUGIN_ID + ".explorer.size"; //$NON-NLS-1S
+ private final static String COLUMN_DATE =
+ DdmsPlugin.PLUGIN_ID + ".explorer.data"; //$NON-NLS-1S
+ private final static String COLUMN_TIME =
+ DdmsPlugin.PLUGIN_ID + ".explorer.time"; //$NON-NLS-1S
+ private final static String COLUMN_PERMISSIONS =
+ DdmsPlugin.PLUGIN_ID + ".explorer.permissions"; //$NON-NLS-1S
+ private final static String COLUMN_INFO =
+ DdmsPlugin.PLUGIN_ID + ".explorer.info"; //$NON-NLS-1$
+
+ private DeviceExplorer mExplorer;
+
+ public FileExplorerView() {
+ }
+
+ @Override
+ public void createPartControl(Composite parent) {
+ ImageLoader loader = ImageLoader.getDdmUiLibLoader();
+
+ DeviceExplorer.COLUMN_NAME = COLUMN_NAME;
+ DeviceExplorer.COLUMN_SIZE = COLUMN_SIZE;
+ DeviceExplorer.COLUMN_DATE = COLUMN_DATE;
+ DeviceExplorer.COLUMN_TIME = COLUMN_TIME;
+ DeviceExplorer.COLUMN_PERMISSIONS = COLUMN_PERMISSIONS;
+ DeviceExplorer.COLUMN_INFO = COLUMN_INFO;
+
+ // device explorer
+ mExplorer = new DeviceExplorer();
+
+ mExplorer.setCustomImages(
+ PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FILE),
+ PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FOLDER),
+ null /* apk image */,
+ PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_ELEMENT)
+ );
+
+ // creates the actions
+ CommonAction pushAction = new CommonAction(Messages.FileExplorerView_Push_File) {
+ @Override
+ public void run() {
+ mExplorer.pushIntoSelection();
+ }
+ };
+ pushAction.setToolTipText(Messages.FileExplorerView_Push_File_Onto_Device);
+ pushAction.setImageDescriptor(loader.loadDescriptor("push.png")); //$NON-NLS-1$
+ pushAction.setEnabled(false);
+
+ CommonAction pullAction = new CommonAction(Messages.FileExplorerView_Pull_File) {
+ @Override
+ public void run() {
+ mExplorer.pullSelection();
+ }
+ };
+ pullAction.setToolTipText(Messages.FileExplorerView_Pull_File_From_File);
+ pullAction.setImageDescriptor(loader.loadDescriptor("pull.png")); //$NON-NLS-1$
+ pullAction.setEnabled(false);
+
+ CommonAction deleteAction = new CommonAction(Messages.FileExplorerView_Delete) {
+ @Override
+ public void run() {
+ mExplorer.deleteSelection();
+ }
+ };
+ deleteAction.setToolTipText(Messages.FileExplorerView_Delete_The_Selection);
+ deleteAction.setImageDescriptor(loader.loadDescriptor("delete.png")); //$NON-NLS-1$
+ deleteAction.setEnabled(false);
+
+ CommonAction createNewFolderAction = new CommonAction("New Folder") {
+ @Override
+ public void run() {
+ mExplorer.createNewFolderInSelection();
+ }
+ };
+ createNewFolderAction.setToolTipText("New Folder");
+ createNewFolderAction.setImageDescriptor(loader.loadDescriptor("add.png")); //$NON-NLS-1$
+ createNewFolderAction.setEnabled(false);
+
+ // set up the actions in the explorer
+ mExplorer.setActions(pushAction, pullAction, deleteAction, createNewFolderAction);
+
+ // and in the ui
+ IActionBars actionBars = getViewSite().getActionBars();
+ IMenuManager menuManager = actionBars.getMenuManager();
+ IToolBarManager toolBarManager = actionBars.getToolBarManager();
+
+ menuManager.add(pullAction);
+ menuManager.add(pushAction);
+ menuManager.add(new Separator());
+ menuManager.add(deleteAction);
+ menuManager.add(new Separator());
+ menuManager.add(createNewFolderAction);
+
+ toolBarManager.add(pullAction);
+ toolBarManager.add(pushAction);
+ toolBarManager.add(new Separator());
+ toolBarManager.add(deleteAction);
+ toolBarManager.add(new Separator());
+ toolBarManager.add(createNewFolderAction);
+
+ mExplorer.createPanel(parent);
+
+ DdmsPlugin.getDefault().addSelectionListener(this);
+ }
+
+ @Override
+ public void setFocus() {
+ mExplorer.setFocus();
+ }
+
+ /**
+ * Sent when a new {@link Client} is selected.
+ *
+ * @param selectedClient The selected client.
+ */
+ @Override
+ public void selectionChanged(Client selectedClient) {
+ // pass
+ }
+
+ /**
+ * Sent when a new {@link Device} is selected.
+ *
+ * @param selectedDevice the selected device.
+ */
+ @Override
+ public void selectionChanged(IDevice selectedDevice) {
+ mExplorer.switchDevice(selectedDevice);
+ }
+
+ /**
+ * Sent when there is no current selection.
+ */
+ public void selectionRemoved() {
+
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/HeapView.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/HeapView.java
new file mode 100644
index 000000000..5745e8efc
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/HeapView.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2007 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.ide.eclipse.ddms.views;
+
+import com.android.ddmuilib.HeapPanel;
+
+import org.eclipse.swt.widgets.Composite;
+
+public class HeapView extends TableView {
+
+ public static final String ID = "com.android.ide.eclipse.ddms.views.HeapView"; //$NON-NLS-1$
+ private HeapPanel mPanel;
+
+ public HeapView() {
+ }
+
+ @Override
+ public void createPartControl(Composite parent) {
+ mPanel = new HeapPanel();
+ mPanel.createPanel(parent);
+
+ setSelectionDependentPanel(mPanel);
+
+ // listen to focus changes for table(s) of the panel.
+ setupTableFocusListener(mPanel, parent);
+ }
+
+ @Override
+ public void setFocus() {
+ mPanel.setFocus();
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/LogCatView.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/LogCatView.java
new file mode 100644
index 000000000..9f78c4aa1
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/LogCatView.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2011 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.ide.eclipse.ddms.views;
+
+import com.android.ddmlib.logcat.LogCatMessage;
+import com.android.ddmuilib.logcat.ILogCatMessageSelectionListener;
+import com.android.ddmuilib.logcat.LogCatPanel;
+import com.android.ddmuilib.logcat.LogCatStackTraceParser;
+import com.android.ide.eclipse.ddms.DdmsPlugin;
+import com.android.ide.eclipse.ddms.JavaSourceRevealer;
+import com.android.ide.eclipse.ddms.i18n.Messages;
+import com.android.ide.eclipse.ddms.preferences.PreferenceInitializer;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.actions.ActionFactory;
+
+public class LogCatView extends SelectionDependentViewPart {
+ /** LogCatView ID as defined in plugin.xml. */
+ public static final String ID = "com.android.ide.eclipse.ddms.views.LogCatView"; //$NON-NLS-1$
+
+ /** Switch perspective when a Java file is opened from logcat view. */
+ public static final boolean DEFAULT_SWITCH_PERSPECTIVE = true;
+
+ /** Target perspective to open when a Java file is opened from logcat view. */
+ public static final String DEFAULT_PERSPECTIVE_ID =
+ "org.eclipse.jdt.ui.JavaPerspective"; //$NON-NLS-1$
+
+ private LogCatPanel mLogCatPanel;
+ private LogCatStackTraceParser mStackTraceParser = new LogCatStackTraceParser();
+
+ private Clipboard mClipboard;
+
+ @Override
+ public void createPartControl(Composite parent) {
+ parent.setLayout(new FillLayout());
+
+ IPreferenceStore prefStore = DdmsPlugin.getDefault().getPreferenceStore();
+ mLogCatPanel = new LogCatPanel(prefStore);
+ mLogCatPanel.createPanel(parent);
+ setSelectionDependentPanel(mLogCatPanel);
+
+ mLogCatPanel.addLogCatMessageSelectionListener(new ILogCatMessageSelectionListener() {
+ @Override
+ public void messageDoubleClicked(LogCatMessage m) {
+ onDoubleClick(m);
+ }
+ });
+
+ mClipboard = new Clipboard(parent.getDisplay());
+ IActionBars actionBars = getViewSite().getActionBars();
+ actionBars.setGlobalActionHandler(ActionFactory.COPY.getId(),
+ new Action(Messages.LogCatView_Copy) {
+ @Override
+ public void run() {
+ mLogCatPanel.copySelectionToClipboard(mClipboard);
+ }
+ });
+
+ actionBars.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(),
+ new Action(Messages.LogCatView_Select_All) {
+ @Override
+ public void run() {
+ mLogCatPanel.selectAll();
+ }
+ });
+
+ actionBars.setGlobalActionHandler(ActionFactory.FIND.getId(),
+ new Action("Find") {
+ @Override
+ public void run() {
+ mLogCatPanel.showFindDialog();
+ }
+ });
+ }
+
+ @Override
+ public void setFocus() {
+ }
+
+ private void onDoubleClick(LogCatMessage m) {
+ String msg = m.getMessage();
+ if (!mStackTraceParser.isValidExceptionTrace(msg)) {
+ return;
+ }
+
+ IPreferenceStore store = DdmsPlugin.getDefault().getPreferenceStore();
+ String perspectiveId = null;
+ if (store.getBoolean(PreferenceInitializer.ATTR_SWITCH_PERSPECTIVE)) {
+ perspectiveId = store.getString(PreferenceInitializer.ATTR_PERSPECTIVE_ID);
+ }
+
+
+ String fileName = mStackTraceParser.getFileName(msg);
+ int lineNumber = mStackTraceParser.getLineNumber(msg);
+ String methodName = mStackTraceParser.getMethodName(msg);
+ JavaSourceRevealer.revealMethod(methodName, fileName, lineNumber, perspectiveId);
+ }
+
+ public void selectTransientAppFilter(String appName) {
+ mLogCatPanel.selectTransientAppFilter(appName);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/NativeHeapView.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/NativeHeapView.java
new file mode 100644
index 000000000..91c8022b7
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/NativeHeapView.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2007 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.ide.eclipse.ddms.views;
+
+import com.android.ddmuilib.NativeHeapPanel;
+
+import org.eclipse.swt.widgets.Composite;
+
+public class NativeHeapView extends TableView {
+
+ public static final String ID =
+ "com.android.ide.eclipse.ddms.views.NativeHeapView"; //$NON-NLS-1$
+ private NativeHeapPanel mPanel;
+
+ public NativeHeapView() {
+ }
+
+ @Override
+ public void createPartControl(Composite parent) {
+ mPanel = new NativeHeapPanel();
+ mPanel.createPanel(parent);
+
+ setSelectionDependentPanel(mPanel);
+
+ // listen to focus changes for table(s) of the panel.
+ setupTableFocusListener(mPanel, parent);
+ }
+
+ @Override
+ public void setFocus() {
+ mPanel.setFocus();
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/NetworkStatisticsView.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/NetworkStatisticsView.java
new file mode 100644
index 000000000..f90189c6d
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/NetworkStatisticsView.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2012 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.ide.eclipse.ddms.views;
+
+import com.android.ddmuilib.net.NetworkPanel;
+
+import org.eclipse.swt.widgets.Composite;
+
+public class NetworkStatisticsView extends TableView {
+ public static final String ID = "com.android.ide.eclipse.ddms.views.NetworkStatsView";
+
+ private NetworkPanel mPanel;
+
+ public NetworkStatisticsView() {
+ }
+
+ @Override
+ public void createPartControl(Composite parent) {
+ mPanel = new NetworkPanel();
+ mPanel.createPanel(parent);
+
+ setSelectionDependentPanel(mPanel);
+
+ // listen to focus changes for table(s) of the panel.
+ setupTableFocusListener(mPanel, parent);
+ }
+
+ @Override
+ public void setFocus() {
+ mPanel.setFocus();
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/OldLogCatView.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/OldLogCatView.java
new file mode 100644
index 000000000..d0b1fb442
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/OldLogCatView.java
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2007 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.ide.eclipse.ddms.views;
+
+import com.android.ddmlib.Log.LogLevel;
+import com.android.ddmuilib.ImageLoader;
+import com.android.ddmuilib.logcat.LogColors;
+import com.android.ddmuilib.logcat.LogFilter;
+import com.android.ddmuilib.logcat.LogPanel;
+import com.android.ddmuilib.logcat.LogPanel.ILogFilterStorageManager;
+import com.android.ddmuilib.logcat.LogPanel.LogCatViewInterface;
+import com.android.ide.eclipse.ddms.CommonAction;
+import com.android.ide.eclipse.ddms.DdmsPlugin;
+import com.android.ide.eclipse.ddms.i18n.Messages;
+import com.android.ide.eclipse.ddms.preferences.PreferenceInitializer;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.IPerspectiveRegistry;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.WorkbenchException;
+import org.eclipse.ui.actions.ActionFactory;
+import org.eclipse.ui.ide.IDE;
+
+import java.util.ArrayList;
+
+/**
+ * The log cat view displays log output from the current device selection.
+ */
+public final class OldLogCatView extends SelectionDependentViewPart implements LogCatViewInterface {
+
+ public static final String ID = "com.android.ide.eclipse.ddms.views.OldLogCatView"; //$NON-NLS-1$
+
+ private static final String PREFS_COL_TIME =
+ DdmsPlugin.PLUGIN_ID + ".logcat.time"; //$NON-NLS-1$
+ private static final String PREFS_COL_LEVEL =
+ DdmsPlugin.PLUGIN_ID + ".logcat.level"; //$NON-NLS-1$
+ private static final String PREFS_COL_PID =
+ DdmsPlugin.PLUGIN_ID + ".logcat.pid"; //$NON-NLS-1$
+ private static final String PREFS_COL_TAG =
+ DdmsPlugin.PLUGIN_ID + ".logcat.tag"; //$NON-NLS-1$
+ private static final String PREFS_COL_MESSAGE =
+ DdmsPlugin.PLUGIN_ID + ".logcat.message"; //$NON-NLS-1$
+
+ private static final String PREFS_FILTERS =
+ DdmsPlugin.PLUGIN_ID + ".logcat.filters"; //$NON-NLS-1$
+
+ public static final String CHOICE_METHOD_DECLARATION =
+ DdmsPlugin.PLUGIN_ID + ".logcat.MethodDeclaration"; //$NON-NLS-1$
+ public static final String CHOICE_ERROR_LINE =
+ DdmsPlugin.PLUGIN_ID + ".logcat.ErrorLine"; //$NON-NLS-1$
+
+ /* Default values for the switch of perspective. */
+ public static final boolean DEFAULT_SWITCH_PERSPECTIVE = true;
+ public static final String DEFAULT_PERSPECTIVE_ID = "org.eclipse.jdt.ui.JavaPerspective"; //$NON-NLS-1$
+ private static OldLogCatView sThis;
+ private LogPanel mLogPanel;
+
+ private CommonAction mCreateFilterAction;
+ private CommonAction mDeleteFilterAction;
+ private CommonAction mEditFilterAction;
+ private CommonAction mExportAction;
+
+ private CommonAction[] mLogLevelActions;
+ private String[] mLogLevelIcons = {
+ "v.png", //$NON-NLS-1S
+ "d.png", //$NON-NLS-1S
+ "i.png", //$NON-NLS-1S
+ "w.png", //$NON-NLS-1S
+ "e.png", //$NON-NLS-1S
+ };
+
+ private Action mClearAction;
+
+ private Clipboard mClipboard;
+
+ /**
+ * An implementation of {@link ILogFilterStorageManager} to bridge to the
+ * eclipse preference store, and saves the log filters.
+ */
+ private final class FilterStorage implements ILogFilterStorageManager {
+
+ @Override
+ public LogFilter[] getFilterFromStore() {
+ String filterPrefs = DdmsPlugin.getDefault().getPreferenceStore().getString(
+ PREFS_FILTERS);
+
+ // split in a string per filter
+ String[] filters = filterPrefs.split("\\|"); //$NON-NLS-1$
+
+ ArrayList<LogFilter> list =
+ new ArrayList<LogFilter>(filters.length);
+
+ for (String f : filters) {
+ if (f.length() > 0) {
+ LogFilter logFilter = new LogFilter();
+ if (logFilter.loadFromString(f)) {
+ list.add(logFilter);
+ }
+ }
+ }
+
+ return list.toArray(new LogFilter[list.size()]);
+ }
+
+ @Override
+ public void saveFilters(LogFilter[] filters) {
+ StringBuilder sb = new StringBuilder();
+ for (LogFilter f : filters) {
+ String filterString = f.toString();
+ sb.append(filterString);
+ sb.append('|');
+ }
+
+ DdmsPlugin.getDefault().getPreferenceStore().setValue(PREFS_FILTERS, sb.toString());
+ }
+
+ @Override
+ public boolean requiresDefaultFilter() {
+ return true;
+ }
+ }
+
+ public OldLogCatView() {
+ sThis = this;
+ LogPanel.PREFS_TIME = PREFS_COL_TIME;
+ LogPanel.PREFS_LEVEL = PREFS_COL_LEVEL;
+ LogPanel.PREFS_PID = PREFS_COL_PID;
+ LogPanel.PREFS_TAG = PREFS_COL_TAG;
+ LogPanel.PREFS_MESSAGE = PREFS_COL_MESSAGE;
+ }
+
+ /**
+ * Returns the singleton instance.
+ */
+ public static OldLogCatView getInstance() {
+ return sThis;
+ }
+
+ /**
+ * Sets the display font.
+ *
+ * @param font The font.
+ */
+ public static void setFont(Font font) {
+ if (sThis != null && sThis.mLogPanel != null) {
+ sThis.mLogPanel.setFont(font);
+ }
+ }
+
+ @Override
+ public void createPartControl(Composite parent) {
+ Display d = parent.getDisplay();
+ LogColors colors = new LogColors();
+
+ ImageLoader loader = ImageLoader.getDdmUiLibLoader();
+
+ colors.infoColor = new Color(d, 0, 127, 0);
+ colors.debugColor = new Color(d, 0, 0, 127);
+ colors.errorColor = new Color(d, 255, 0, 0);
+ colors.warningColor = new Color(d, 255, 127, 0);
+ colors.verboseColor = new Color(d, 0, 0, 0);
+
+ mCreateFilterAction = new CommonAction(Messages.LogCatView_Create_Filter) {
+ @Override
+ public void run() {
+ mLogPanel.addFilter();
+ }
+ };
+ mCreateFilterAction.setToolTipText(Messages.LogCatView_Create_Filter_Tooltip);
+ mCreateFilterAction.setImageDescriptor(loader.loadDescriptor("add.png")); //$NON-NLS-1$
+
+ mEditFilterAction = new CommonAction(Messages.LogCatView_Edit_Filter) {
+ @Override
+ public void run() {
+ mLogPanel.editFilter();
+ }
+ };
+ mEditFilterAction.setToolTipText(Messages.LogCatView_Edit_Filter_Tooltip);
+ mEditFilterAction.setImageDescriptor(loader.loadDescriptor("edit.png")); //$NON-NLS-1$
+
+ mDeleteFilterAction = new CommonAction(Messages.LogCatView_Delete_Filter) {
+ @Override
+ public void run() {
+ mLogPanel.deleteFilter();
+ }
+ };
+ mDeleteFilterAction.setToolTipText(Messages.LogCatView_Delete_Filter_Tooltip);
+ mDeleteFilterAction.setImageDescriptor(loader.loadDescriptor("delete.png")); //$NON-NLS-1$
+
+ mExportAction = new CommonAction(Messages.LogCatView_Export_Selection_As_Text) {
+ @Override
+ public void run() {
+ mLogPanel.save();
+ }
+ };
+ mExportAction.setToolTipText(Messages.LogCatView_Export_Selection_As_Text_Tooltip);
+ mExportAction.setImageDescriptor(loader.loadDescriptor("save.png")); //$NON-NLS-1$
+
+ LogLevel[] levels = LogLevel.values();
+ mLogLevelActions = new CommonAction[mLogLevelIcons.length];
+ for (int i = 0; i < mLogLevelActions.length; i++) {
+ String name = levels[i].getStringValue();
+ mLogLevelActions[i] = new CommonAction(name, IAction.AS_CHECK_BOX) {
+ @Override
+ public void run() {
+ // disable the other actions and record current index
+ for (int j = 0; j < mLogLevelActions.length; j++) {
+ Action a = mLogLevelActions[j];
+ if (a == this) {
+ a.setChecked(true);
+
+ // set the log level
+ mLogPanel.setCurrentFilterLogLevel(j + 2);
+ } else {
+ a.setChecked(false);
+ }
+ }
+ }
+ };
+
+ mLogLevelActions[i].setToolTipText(name);
+ mLogLevelActions[i].setImageDescriptor(loader.loadDescriptor(mLogLevelIcons[i]));
+ }
+
+ mClearAction = new Action(Messages.LogCatView_Clear_Log) {
+ @Override
+ public void run() {
+ mLogPanel.clear();
+ }
+ };
+ mClearAction.setImageDescriptor(loader.loadDescriptor("clear.png")); //$NON-NLS-1$
+
+ // now create the log view
+ mLogPanel = new LogPanel(colors, new FilterStorage(), LogPanel.FILTER_MANUAL);
+ mLogPanel.setLogCatViewInterface(this);
+ mLogPanel.setActions(mDeleteFilterAction, mEditFilterAction, mLogLevelActions);
+
+ // get the font
+ String fontStr = DdmsPlugin.getDefault().getPreferenceStore().getString(
+ PreferenceInitializer.ATTR_LOGCAT_FONT);
+ if (fontStr != null) {
+ FontData data = new FontData(fontStr);
+
+ if (fontStr != null) {
+ mLogPanel.setFont(new Font(parent.getDisplay(), data));
+ }
+ }
+
+ mLogPanel.createPanel(parent);
+ setSelectionDependentPanel(mLogPanel);
+
+ // place the actions.
+ placeActions();
+
+ // setup the copy action
+ mClipboard = new Clipboard(d);
+ IActionBars actionBars = getViewSite().getActionBars();
+ actionBars.setGlobalActionHandler(ActionFactory.COPY.getId(), new Action(
+ Messages.LogCatView_Copy) {
+ @Override
+ public void run() {
+ mLogPanel.copy(mClipboard);
+ }
+ });
+
+ // setup the select all action
+ actionBars.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(),
+ new Action(Messages.LogCatView_Select_All) {
+ @Override
+ public void run() {
+ mLogPanel.selectAll();
+ }
+ });
+ }
+
+ @Override
+ public void dispose() {
+ mLogPanel.stopLogCat(true);
+ mClipboard.dispose();
+ }
+
+ @Override
+ public void setFocus() {
+ mLogPanel.setFocus();
+ }
+
+ /**
+ * Place the actions in the ui.
+ */
+ private void placeActions() {
+ IActionBars actionBars = getViewSite().getActionBars();
+
+ // first in the menu
+ IMenuManager menuManager = actionBars.getMenuManager();
+ menuManager.add(mCreateFilterAction);
+ menuManager.add(mEditFilterAction);
+ menuManager.add(mDeleteFilterAction);
+ menuManager.add(new Separator());
+ menuManager.add(mClearAction);
+ menuManager.add(new Separator());
+ menuManager.add(mExportAction);
+
+ // and then in the toolbar
+ IToolBarManager toolBarManager = actionBars.getToolBarManager();
+ for (CommonAction a : mLogLevelActions) {
+ toolBarManager.add(a);
+ }
+ toolBarManager.add(new Separator());
+ toolBarManager.add(mCreateFilterAction);
+ toolBarManager.add(mEditFilterAction);
+ toolBarManager.add(mDeleteFilterAction);
+ toolBarManager.add(new Separator());
+ toolBarManager.add(mClearAction);
+ }
+
+ void openFile(IFile file, IMarker marker) {
+ try {
+ IWorkbenchPage page = getViewSite().getWorkbenchWindow()
+ .getActivePage();
+ if (page != null) {
+ IDE.openEditor(page, marker);
+ marker.delete();
+ }
+ } catch (CoreException e) {
+ Status s = new Status(Status.ERROR, DdmsPlugin.PLUGIN_ID, e.getMessage(), e);
+ DdmsPlugin.getDefault().getLog().log(s);
+ }
+ }
+
+ void switchPerspective() {
+ IPreferenceStore store = DdmsPlugin.getDefault().getPreferenceStore();
+ if (store.getBoolean(PreferenceInitializer.ATTR_SWITCH_PERSPECTIVE)) {
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
+ IPerspectiveRegistry perspectiveRegistry = workbench.getPerspectiveRegistry();
+ String perspectiveId = store.getString(PreferenceInitializer.ATTR_PERSPECTIVE_ID);
+ if (perspectiveId != null
+ && perspectiveId.length() > 0
+ && perspectiveRegistry.findPerspectiveWithId(perspectiveId) != null) {
+ try {
+ workbench.showPerspective(perspectiveId, window);
+ } catch (WorkbenchException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onDoubleClick() {
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/SelectionDependentViewPart.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/SelectionDependentViewPart.java
new file mode 100644
index 000000000..3326d0138
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/SelectionDependentViewPart.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2007 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.ide.eclipse.ddms.views;
+
+import com.android.ddmlib.Client;
+import com.android.ddmlib.IDevice;
+import com.android.ddmuilib.SelectionDependentPanel;
+import com.android.ide.eclipse.ddms.DdmsPlugin;
+import com.android.ide.eclipse.ddms.DdmsPlugin.ISelectionListener;
+
+import org.eclipse.swt.graphics.Device;
+import org.eclipse.ui.part.ViewPart;
+
+/**
+ * A Workbench {@link ViewPart} that requires {@link Device}/{@link Client} selection notifications
+ * from {@link DdmsPlugin} through the {@link ISelectionListener} interface.
+ */
+public abstract class SelectionDependentViewPart extends ViewPart implements ISelectionListener {
+
+ private SelectionDependentPanel mPanel;
+
+ protected final void setSelectionDependentPanel(SelectionDependentPanel panel) {
+ // remember the panel
+ mPanel = panel;
+
+ // and add ourself as listener of selection events.
+ DdmsPlugin.getDefault().addSelectionListener(this);
+ }
+
+ @Override
+ public void dispose() {
+ DdmsPlugin.getDefault().removeSelectionListener(this);
+ super.dispose();
+ }
+
+ /**
+ * Sent when a new {@link Client} is selected.
+ * @param selectedClient The selected client.
+ *
+ * @see ISelectionListener
+ */
+ @Override
+ public final void selectionChanged(Client selectedClient) {
+ mPanel.clientSelected(selectedClient);
+ }
+
+ /**
+ * Sent when a new {@link Device} is selected.
+ * @param selectedDevice the selected device.
+ *
+ * @see ISelectionListener
+ */
+ @Override
+ public final void selectionChanged(IDevice selectedDevice) {
+ mPanel.deviceSelected(selectedDevice);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/SysInfoView.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/SysInfoView.java
new file mode 100644
index 000000000..e4939250e
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/SysInfoView.java
@@ -0,0 +1,29 @@
+package com.android.ide.eclipse.ddms.views;
+
+import com.android.ddmuilib.SysinfoPanel;
+
+import org.eclipse.swt.widgets.Composite;
+
+public class SysInfoView extends SelectionDependentViewPart {
+ public static final String ID = "com.android.ide.eclipse.ddms.views.SysInfoView"; //$NON-NLS-1$
+
+ private SysinfoPanel mSysInfoPanel;
+
+ @Override
+ public void createPartControl(Composite parent) {
+ mSysInfoPanel = new SysinfoPanel();
+ mSysInfoPanel.createPanel(parent);
+ setSelectionDependentPanel(mSysInfoPanel);
+ }
+
+ @Override
+ public void setFocus() {
+ mSysInfoPanel.setFocus();
+ }
+
+ @Override
+ public void dispose() {
+ mSysInfoPanel.dispose();
+ super.dispose();
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/TableView.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/TableView.java
new file mode 100644
index 000000000..1f9f0db53
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/TableView.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2007 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.ide.eclipse.ddms.views;
+
+import com.android.ddmuilib.ITableFocusListener;
+import com.android.ddmuilib.ITableFocusListener.IFocusedTableActivator;
+import com.android.ddmuilib.TablePanel;
+import com.android.ide.eclipse.ddms.i18n.Messages;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.actions.ActionFactory;
+
+/**
+ * Base class for view containing Table that needs to support copy, and select
+ * all.
+ */
+public abstract class TableView extends SelectionDependentViewPart {
+
+ /** Activator for the current Table that has the focus */
+ IFocusedTableActivator mActivator = null;
+
+ private Clipboard mClipboard;
+
+ private Action mCopyAction;
+ private Action mSelectAllAction;
+
+ /**
+ * Setup the listener for the Table objects of <code>Panel</code>, and setup
+ * the copy and select all actions.
+ *
+ * @param panel The panel to setup
+ * @param parent The parent composite of the Panel's content.
+ */
+ void setupTableFocusListener(TablePanel panel, Composite parent) {
+ panel.setTableFocusListener(new ITableFocusListener() {
+ @Override
+ public void focusGained(IFocusedTableActivator activator) {
+ mActivator = activator;
+ mCopyAction.setEnabled(true);
+ mSelectAllAction.setEnabled(true);
+ }
+
+ @Override
+ public void focusLost(IFocusedTableActivator activator) {
+ if (activator == mActivator) {
+ mActivator = null;
+ mCopyAction.setEnabled(false);
+ mSelectAllAction.setEnabled(false);
+ }
+ }
+ });
+
+ // setup the copy action
+ mClipboard = new Clipboard(parent.getDisplay());
+ IActionBars actionBars = getViewSite().getActionBars();
+ actionBars.setGlobalActionHandler(ActionFactory.COPY.getId(),
+ mCopyAction = new Action(Messages.TableView_Copy) {
+ @Override
+ public void run() {
+ if (mActivator != null) {
+ mActivator.copy(mClipboard);
+ }
+ }
+ });
+
+ // setup the select all action
+ actionBars.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(),
+ mSelectAllAction = new Action(Messages.TableView_Select_All) {
+ @Override
+ public void run() {
+ if (mActivator != null) {
+ mActivator.selectAll();
+ }
+ }
+ });
+
+ }
+
+ @Override
+ public void dispose() {
+ super.dispose();
+ mClipboard.dispose();
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/ThreadView.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/ThreadView.java
new file mode 100644
index 000000000..9d4eeb74a
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/ThreadView.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2007 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.ide.eclipse.ddms.views;
+
+import com.android.ddmuilib.ThreadPanel;
+
+import org.eclipse.swt.widgets.Composite;
+
+public class ThreadView extends TableView {
+
+ public static final String ID =
+ "com.android.ide.eclipse.ddms.views.ThreadView"; //$NON-NLS-1$
+ private ThreadPanel mPanel;
+
+ public ThreadView() {
+ }
+
+ @Override
+ public void createPartControl(Composite parent) {
+ mPanel = new ThreadPanel();
+ mPanel.createPanel(parent);
+
+ setSelectionDependentPanel(mPanel);
+
+ // listen to focus changes for table(s) of the panel.
+ setupTableFocusListener(mPanel, parent);
+ }
+
+ @Override
+ public void setFocus() {
+ mPanel.setFocus();
+ }
+}