diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.ddms/src/com')
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(); + } +} |