/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php * * 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.adt.launch; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.common.project.AndroidManifestParser; import com.android.ide.eclipse.common.project.BaseProjectHelper; import com.android.ide.eclipse.common.project.ProjectChooserHelper; import com.android.ide.eclipse.common.project.AndroidManifestParser.Activity; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.ui.AbstractLaunchConfigurationTab; import org.eclipse.debug.ui.ILaunchConfigurationTab; import org.eclipse.jdt.core.IJavaModel; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants; 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.graphics.Font; import org.eclipse.swt.graphics.Image; 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.Group; import org.eclipse.swt.widgets.Text; import java.util.ArrayList; /** * Class for the main launch configuration tab. */ public class MainLaunchConfigTab extends AbstractLaunchConfigurationTab { /** * */ public static final String LAUNCH_TAB_IMAGE = "mainLaunchTab.png"; //$NON-NLS-1$ protected static final String EMPTY_STRING = ""; //$NON-NLS-1$ protected Text mProjText; private Button mProjButton; private Combo mActivityCombo; private final ArrayList<Activity> mActivities = new ArrayList<Activity>(); private WidgetListener mListener = new WidgetListener(); private Button mDefaultActionButton; private Button mActivityActionButton; private Button mDoNothingActionButton; private int mLaunchAction = LaunchConfigDelegate.DEFAULT_LAUNCH_ACTION; private ProjectChooserHelper mProjectChooserHelper; /** * A listener which handles widget change events for the controls in this * tab. */ private class WidgetListener implements ModifyListener, SelectionListener { public void modifyText(ModifyEvent e) { IProject project = checkParameters(); loadActivities(project); setDirty(true); } public void widgetDefaultSelected(SelectionEvent e) {/* do nothing */ } public void widgetSelected(SelectionEvent e) { Object source = e.getSource(); if (source == mProjButton) { handleProjectButtonSelected(); } else { checkParameters(); } } } public MainLaunchConfigTab() { } public void createControl(Composite parent) { mProjectChooserHelper = new ProjectChooserHelper(parent.getShell()); Font font = parent.getFont(); Composite comp = new Composite(parent, SWT.NONE); setControl(comp); GridLayout topLayout = new GridLayout(); topLayout.verticalSpacing = 0; comp.setLayout(topLayout); comp.setFont(font); createProjectEditor(comp); createVerticalSpacer(comp, 1); // create the combo for the activity chooser Group group = new Group(comp, SWT.NONE); group.setText("Launch Action:"); GridData gd = new GridData(GridData.FILL_HORIZONTAL); group.setLayoutData(gd); GridLayout layout = new GridLayout(); layout.numColumns = 2; group.setLayout(layout); group.setFont(font); mDefaultActionButton = new Button(group, SWT.RADIO); gd = new GridData(GridData.FILL_HORIZONTAL); gd.horizontalSpan = 2; mDefaultActionButton.setLayoutData(gd); mDefaultActionButton.setText("Launch Default Activity"); mDefaultActionButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { // event are received for both selection and deselection, so we only process // the selection event to avoid doing it twice. if (mDefaultActionButton.getSelection() == true) { mLaunchAction = LaunchConfigDelegate.ACTION_DEFAULT; mActivityCombo.setEnabled(false); checkParameters(); } } }); mActivityActionButton = new Button(group, SWT.RADIO); mActivityActionButton.setText("Launch:"); mActivityActionButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { // event are received for both selection and deselection, so we only process // the selection event to avoid doing it twice. if (mActivityActionButton.getSelection() == true) { mLaunchAction = LaunchConfigDelegate.ACTION_ACTIVITY; mActivityCombo.setEnabled(true); checkParameters(); } } }); mActivityCombo = new Combo(group, SWT.DROP_DOWN | SWT.READ_ONLY); gd = new GridData(GridData.FILL_HORIZONTAL); mActivityCombo.setLayoutData(gd); mActivityCombo.clearSelection(); mActivityCombo.setEnabled(false); mActivityCombo.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { checkParameters(); } }); mDoNothingActionButton = new Button(group, SWT.RADIO); gd = new GridData(GridData.FILL_HORIZONTAL); gd.horizontalSpan = 2; mDoNothingActionButton.setLayoutData(gd); mDoNothingActionButton.setText("Do Nothing"); mDoNothingActionButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { // event are received for both selection and deselection, so we only process // the selection event to avoid doing it twice. if (mDoNothingActionButton.getSelection() == true) { mLaunchAction = LaunchConfigDelegate.ACTION_DO_NOTHING; mActivityCombo.setEnabled(false); checkParameters(); } } }); } public String getName() { return "Android"; } @Override public Image getImage() { return AdtPlugin.getImageLoader().loadImage(LAUNCH_TAB_IMAGE, null); } public void performApply(ILaunchConfigurationWorkingCopy configuration) { configuration.setAttribute( IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, mProjText.getText()); configuration.setAttribute( IJavaLaunchConfigurationConstants.ATTR_ALLOW_TERMINATE, true); // add the launch mode configuration.setAttribute(LaunchConfigDelegate.ATTR_LAUNCH_ACTION, mLaunchAction); // add the activity int selection = mActivityCombo.getSelectionIndex(); if (mActivities != null && selection >=0 && selection < mActivities.size()) { configuration.setAttribute(LaunchConfigDelegate.ATTR_ACTIVITY, mActivities.get(selection).getName()); } // link the project and the launch config. mapResources(configuration); } public void setDefaults(ILaunchConfigurationWorkingCopy configuration) { configuration.setAttribute(LaunchConfigDelegate.ATTR_LAUNCH_ACTION, LaunchConfigDelegate.DEFAULT_LAUNCH_ACTION); } /** * Creates the widgets for specifying a main type. * * @param parent the parent composite */ protected void createProjectEditor(Composite parent) { Font font = parent.getFont(); Group group = new Group(parent, SWT.NONE); group.setText("Project:"); GridData gd = new GridData(GridData.FILL_HORIZONTAL); group.setLayoutData(gd); GridLayout layout = new GridLayout(); layout.numColumns = 2; group.setLayout(layout); group.setFont(font); mProjText = new Text(group, SWT.SINGLE | SWT.BORDER); gd = new GridData(GridData.FILL_HORIZONTAL); mProjText.setLayoutData(gd); mProjText.setFont(font); mProjText.addModifyListener(mListener); mProjButton = createPushButton(group, "Browse...", null); mProjButton.addSelectionListener(mListener); } /** * returns the default listener from this class. For all subclasses this * listener will only provide the functi Jaonality of updating the current * tab * * @return a widget listener */ protected WidgetListener getDefaultListener() { return mListener; } /** * Return the {@link IJavaProject} corresponding to the project name in the project * name text field, or null if the text does not match a project name. * @param javaModel the Java Model object corresponding for the current workspace root. * @return a IJavaProject object or null. */ protected IJavaProject getJavaProject(IJavaModel javaModel) { String projectName = mProjText.getText().trim(); if (projectName.length() < 1) { return null; } return javaModel.getJavaProject(projectName); } /** * Show a dialog that lets the user select a project. This in turn provides * context for the main type, allowing the user to key a main type name, or * constraining the search for main types to the specified project. */ protected void handleProjectButtonSelected() { IJavaProject javaProject = mProjectChooserHelper.chooseJavaProject( mProjText.getText().trim()); if (javaProject == null) { return; }// end if String projectName = javaProject.getElementName(); mProjText.setText(projectName); // get the list of activities and fill the combo IProject project = javaProject.getProject(); loadActivities(project); }// end handle selected /** * Initializes this tab's controls with values from the given * launch configuration. This method is called when * a configuration is selected to view or edit, after this * tab's control has been created. * * @param config launch configuration * * @see ILaunchConfigurationTab */ public void initializeFrom(ILaunchConfiguration config) { String projectName = EMPTY_STRING; try { projectName = config.getAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, EMPTY_STRING); }// end try catch (CoreException ce) { } mProjText.setText(projectName); IProject proj = mProjectChooserHelper.getAndroidProject(projectName); loadActivities(proj); // load the launch action. mLaunchAction = LaunchConfigDelegate.DEFAULT_LAUNCH_ACTION; try { mLaunchAction = config.getAttribute(LaunchConfigDelegate.ATTR_LAUNCH_ACTION, mLaunchAction); } catch (CoreException e) { // nothing to be done really. launchAction will keep its default value. } mDefaultActionButton.setSelection(mLaunchAction == LaunchConfigDelegate.ACTION_DEFAULT); mActivityActionButton.setSelection(mLaunchAction == LaunchConfigDelegate.ACTION_ACTIVITY); mDoNothingActionButton.setSelection( mLaunchAction == LaunchConfigDelegate.ACTION_DO_NOTHING); // now look for the activity and load it if present, otherwise, revert // to the current one. String activityName = EMPTY_STRING; try { activityName = config.getAttribute(LaunchConfigDelegate.ATTR_ACTIVITY, EMPTY_STRING); }// end try catch (CoreException ce) { // nothing to be done really. activityName will stay empty } if (mLaunchAction != LaunchConfigDelegate.ACTION_ACTIVITY) { mActivityCombo.setEnabled(false); mActivityCombo.clearSelection(); } else { mActivityCombo.setEnabled(true); if (activityName == null || activityName.equals(EMPTY_STRING)) { mActivityCombo.clearSelection(); } else if (mActivities != null && mActivities.size() > 0) { // look for the name of the activity in the combo. boolean found = false; for (int i = 0 ; i < mActivities.size() ; i++) { if (activityName.equals(mActivities.get(i).getName())) { found = true; mActivityCombo.select(i); break; } } // if we haven't found a matching activity we clear the combo selection if (found == false) { mActivityCombo.clearSelection(); } } } } /** * Associates the launch config and the project. This allows Eclipse to delete the launch * config when the project is deleted. * * @param config the launch config working copy. */ protected void mapResources(ILaunchConfigurationWorkingCopy config) { // get the java model IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); IJavaModel javaModel = JavaCore.create(workspaceRoot); // get the IJavaProject described by the text field. IJavaProject javaProject = getJavaProject(javaModel); IResource[] resources = null; if (javaProject != null) { resources = AndroidLaunchController.getResourcesToMap(javaProject.getProject()); } config.setMappedResources(resources); } /** * Loads the ui with the activities of the specified project, and stores the * activities in <code>mActivities</code>. * <p/> * First activity is selected by default if present. * * @param project The project to load the activities from. */ private void loadActivities(IProject project) { if (project != null) { try { // parse the manifest for the list of activities. AndroidManifestParser manifestParser = AndroidManifestParser.parse( BaseProjectHelper.getJavaProject(project), null /* errorListener */, true /* gatherData */, false /* markErrors */); if (manifestParser != null) { Activity[] activities = manifestParser.getActivities(); mActivities.clear(); mActivityCombo.removeAll(); for (Activity activity : activities) { if (activity.isExported() && activity.hasAction()) { mActivities.add(activity); mActivityCombo.add(activity.getName()); } } if (mActivities.size() > 0) { if (mLaunchAction == LaunchConfigDelegate.ACTION_ACTIVITY) { mActivityCombo.setEnabled(true); } } else { mActivityCombo.setEnabled(false); } // the selection will be set when we update the ui from the current // config object. mActivityCombo.clearSelection(); return; } } catch (CoreException e) { // The AndroidManifest parsing failed. The builders must have reported the errors // already so there's nothing to do. } } // if we reach this point, either project is null, or we got an exception during // the parsing. In either case, we empty the activity list. mActivityCombo.removeAll(); mActivities.clear(); } /** * Checks the parameters for correctness, and update the error message and buttons. * @return the current IProject of this launch config. */ private IProject checkParameters() { try { //test the project name first! String text = mProjText.getText(); if (text.length() == 0) { setErrorMessage("Project Name is required!"); } else if (text.matches("[a-zA-Z0-9_ \\.-]+") == false) { setErrorMessage("Project name contains unsupported characters!"); } else { IJavaProject[] projects = mProjectChooserHelper.getAndroidProjects(null); IProject found = null; for (IJavaProject javaProject : projects) { if (javaProject.getProject().getName().equals(text)) { found = javaProject.getProject(); break; } } if (found != null) { setErrorMessage(null); } else { setErrorMessage(String.format("There is no android project named '%1$s'", text)); } return found; } } finally { updateLaunchConfigurationDialog(); } return null; } }