/* * 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.gltrace; import com.android.ddmlib.AndroidDebugBridge; import com.android.ddmlib.IDevice; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.core.runtime.preferences.InstanceScope; import org.eclipse.jface.dialogs.IDialogConstants; 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.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.Display; import org.eclipse.swt.widgets.FileDialog; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import org.osgi.service.prefs.BackingStoreException; import java.io.File; import java.util.ArrayList; import java.util.List; /** Dialog displaying all the trace options before the user initiates tracing. */ public class GLTraceOptionsDialog extends TitleAreaDialog { private static final String TITLE = "OpenGL ES Trace Options"; private static final String DEFAULT_MESSAGE = "Provide the application and activity to be traced."; private static final String PREF_APP_PACKAGE = "gl.trace.apppackage"; //$NON-NLS-1$ private static final String PREF_ACTIVITY = "gl.trace.activity"; //$NON-NLS-1$ private static final String PREF_TRACEFILE = "gl.trace.destfile"; //$NON-NLS-1$ private static final String PREF_DEVICE = "gl.trace.device"; //$NON-NLS-1$ private String mLastUsedDevice; private static String sSaveToFolder = System.getProperty("user.home"); //$NON-NLS-1$ private Button mOkButton; private Combo mDeviceCombo; private Text mAppPackageToTraceText; private Text mActivityToTraceText; private Button mIsActivityFullyQualifiedButton; private Text mTraceFilePathText; private String mSelectedDevice = ""; private String mAppPackageToTrace = ""; private String mActivityToTrace = ""; private String mTraceFilePath = ""; private static boolean sCollectFbOnEglSwap = true; private static boolean sCollectFbOnGlDraw = false; private static boolean sCollectTextureData = false; private static boolean sIsActivityFullyQualified = false; private IDevice[] mDevices; public GLTraceOptionsDialog(Shell parentShell) { super(parentShell); loadPreferences(); } @Override protected Control createDialogArea(Composite shell) { setTitle(TITLE); setMessage(DEFAULT_MESSAGE); Composite parent = (Composite) super.createDialogArea(shell); Composite c = new Composite(parent, SWT.BORDER); c.setLayout(new GridLayout(2, false)); c.setLayoutData(new GridData(GridData.FILL_BOTH)); createLabel(c, "Device:"); mDevices = AndroidDebugBridge.getBridge().getDevices(); createDeviceDropdown(c, mDevices); createSeparator(c); createLabel(c, "Application Package:"); createAppToTraceText(c, "e.g. com.example.package"); createLabel(c, "Activity to launch:"); createActivityToTraceText(c, "Leave blank to launch default activity"); createLabel(c, ""); createIsFullyQualifedActivityButton(c, "Activity name is fully qualified, do not prefix with package name"); createSeparator(c); createLabel(c, "Data Collection Options:"); createCaptureImageOptions(c); createSeparator(c); createLabel(c, "Destination File: "); createSaveToField(c); return c; } @Override protected void createButtonsForButtonBar(Composite parent) { super.createButtonsForButtonBar(parent); mOkButton = getButton(IDialogConstants.OK_ID); mOkButton.setText("Trace"); DialogStatus status = validateDialog(); mOkButton.setEnabled(status.valid); } private void createSeparator(Composite c) { Label l = new Label(c, SWT.SEPARATOR | SWT.HORIZONTAL); GridData gd = new GridData(GridData.FILL_HORIZONTAL); gd.horizontalSpan = 2; l.setLayoutData(gd); } private void createSaveToField(Composite parent) { Composite c = new Composite(parent, SWT.NONE); c.setLayout(new GridLayout(2, false)); c.setLayoutData(new GridData(GridData.FILL_BOTH)); mTraceFilePathText = new Text(c, SWT.BORDER); mTraceFilePathText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); mTraceFilePathText.setText(mTraceFilePath); mTraceFilePathText.addModifyListener(new ModifyListener() { @Override public void modifyText(ModifyEvent e) { validateAndSetMessage(); } }); Button browse = new Button(c, SWT.PUSH); browse.setText("Browse..."); browse.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { String fName = openBrowseDialog(); if (fName == null) { return; } mTraceFilePathText.setText(fName); validateAndSetMessage(); } }); } private String openBrowseDialog() { FileDialog fd = new FileDialog(Display.getDefault().getActiveShell(), SWT.SAVE); fd.setText("Save To"); fd.setFileName("trace1.gltrace"); fd.setFilterPath(sSaveToFolder); fd.setFilterExtensions(new String[] { "*.gltrace" }); String fname = fd.open(); if (fname == null || fname.trim().length() == 0) { return null; } sSaveToFolder = fd.getFilterPath(); return fname; } /** Options controlling when the FB should be captured. */ private void createCaptureImageOptions(Composite parent) { Composite c = new Composite(parent, SWT.NONE); c.setLayout(new GridLayout(1, false)); c.setLayoutData(new GridData(GridData.FILL_BOTH)); final Button readFbOnEglSwapCheckBox = new Button(c, SWT.CHECK); readFbOnEglSwapCheckBox.setText("Read back framebuffer 0 on eglSwapBuffers()"); readFbOnEglSwapCheckBox.setSelection(sCollectFbOnEglSwap); final Button readFbOnGlDrawCheckBox = new Button(c, SWT.CHECK); readFbOnGlDrawCheckBox.setText("Read back currently bound framebuffer On glDraw*()"); readFbOnGlDrawCheckBox.setSelection(sCollectFbOnGlDraw); final Button readTextureDataCheckBox = new Button(c, SWT.CHECK); readTextureDataCheckBox.setText("Collect texture data submitted using glTexImage*()"); readTextureDataCheckBox.setSelection(sCollectTextureData); SelectionListener l = new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { sCollectFbOnEglSwap = readFbOnEglSwapCheckBox.getSelection(); sCollectFbOnGlDraw = readFbOnGlDrawCheckBox.getSelection(); sCollectTextureData = readTextureDataCheckBox.getSelection(); } }; readFbOnEglSwapCheckBox.addSelectionListener(l); readFbOnGlDrawCheckBox.addSelectionListener(l); readTextureDataCheckBox.addSelectionListener(l); } private Text createAppToTraceText(Composite parent, String defaultMessage) { mAppPackageToTraceText = new Text(parent, SWT.BORDER); mAppPackageToTraceText.setMessage(defaultMessage); mAppPackageToTraceText.setText(mAppPackageToTrace); mAppPackageToTraceText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); mAppPackageToTraceText.addModifyListener(new ModifyListener() { @Override public void modifyText(ModifyEvent e) { validateAndSetMessage(); } }); return mActivityToTraceText; } private Text createActivityToTraceText(Composite parent, String defaultMessage) { mActivityToTraceText = new Text(parent, SWT.BORDER); mActivityToTraceText.setMessage(defaultMessage); mActivityToTraceText.setText(mActivityToTrace); mActivityToTraceText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); mActivityToTraceText.addModifyListener(new ModifyListener() { @Override public void modifyText(ModifyEvent e) { validateAndSetMessage(); } }); return mActivityToTraceText; } private Button createIsFullyQualifedActivityButton(Composite parent, String message) { mIsActivityFullyQualifiedButton = new Button(parent, SWT.CHECK); mIsActivityFullyQualifiedButton.setText(message); mIsActivityFullyQualifiedButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); mIsActivityFullyQualifiedButton.setSelection(sIsActivityFullyQualified); return mIsActivityFullyQualifiedButton; } private void validateAndSetMessage() { DialogStatus status = validateDialog(); mOkButton.setEnabled(status.valid); setErrorMessage(status.message); } private Combo createDeviceDropdown(Composite parent, IDevice[] devices) { mDeviceCombo = new Combo(parent, SWT.READ_ONLY | SWT.BORDER); List<String> items = new ArrayList<String>(devices.length); for (IDevice d : devices) { items.add(d.getName()); } mDeviceCombo.setItems(items.toArray(new String[items.size()])); int index = 0; if (items.contains(mLastUsedDevice)) { index = items.indexOf(mLastUsedDevice); } if (index >= 0 && index < items.size()) { mDeviceCombo.select(index); } return mDeviceCombo; } private void createLabel(Composite parent, String text) { Label l = new Label(parent, SWT.NONE); l.setText(text); GridData gd = new GridData(); gd.horizontalAlignment = SWT.RIGHT; gd.verticalAlignment = SWT.CENTER; l.setLayoutData(gd); } /** * A tuple that specifies whether the current state of the inputs * on the dialog is valid or not. If it is not valid, the message * field stores the reason why it isn't. */ private final class DialogStatus { final boolean valid; final String message; private DialogStatus(boolean isValid, String errMessage) { valid = isValid; message = errMessage; } } private DialogStatus validateDialog() { if (mDevices.length == 0) { return new DialogStatus(false, "No connected devices."); } if (mAppPackageToTraceText.getText().trim().isEmpty()) { return new DialogStatus(false, "Provide an application name"); } String traceFile = mTraceFilePathText.getText().trim(); if (traceFile.isEmpty()) { return new DialogStatus(false, "Specify the location where the trace will be saved."); } File f = new File(traceFile).getParentFile(); if (f != null && !f.exists()) { return new DialogStatus(false, String.format("Folder %s does not exist", f.getAbsolutePath())); } return new DialogStatus(true, null); } @Override protected void okPressed() { mAppPackageToTrace = mAppPackageToTraceText.getText().trim(); mActivityToTrace = mActivityToTraceText.getText().trim(); if (mActivityToTrace.startsWith(".")) { //$NON-NLS-1$ mActivityToTrace = mActivityToTrace.substring(1); } sIsActivityFullyQualified = mIsActivityFullyQualifiedButton.getSelection(); mTraceFilePath = mTraceFilePathText.getText().trim(); mSelectedDevice = mDeviceCombo.getText(); savePreferences(); super.okPressed(); } private void savePreferences() { IEclipsePreferences prefs = new InstanceScope().getNode(GlTracePlugin.PLUGIN_ID); prefs.put(PREF_APP_PACKAGE, mAppPackageToTrace); prefs.put(PREF_ACTIVITY, mActivityToTrace); prefs.put(PREF_TRACEFILE, mTraceFilePath); prefs.put(PREF_DEVICE, mSelectedDevice); try { prefs.flush(); } catch (BackingStoreException e) { // ignore issues while persisting preferences } } private void loadPreferences() { IEclipsePreferences prefs = new InstanceScope().getNode(GlTracePlugin.PLUGIN_ID); mAppPackageToTrace = prefs.get(PREF_APP_PACKAGE, ""); mActivityToTrace = prefs.get(PREF_ACTIVITY, ""); mTraceFilePath = prefs.get(PREF_TRACEFILE, ""); mLastUsedDevice = prefs.get(PREF_DEVICE, ""); } public TraceOptions getTraceOptions() { return new TraceOptions(mSelectedDevice, mAppPackageToTrace, mActivityToTrace, sIsActivityFullyQualified, mTraceFilePath, sCollectFbOnEglSwap, sCollectFbOnGlDraw, sCollectTextureData); } }