Home | History | Annotate | Download | only in launch
      1 /*
      2  * Copyright (C) 2007 The Android Open Source Project
      3  *
      4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.eclipse.org/org/documents/epl-v10.php
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.ide.eclipse.adt.internal.launch;
     18 
     19 import com.android.ide.eclipse.adt.internal.editors.IconFactory;
     20 import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper;
     21 import com.android.ide.eclipse.adt.internal.project.ProjectChooserHelper;
     22 import com.android.ide.eclipse.adt.internal.project.ProjectChooserHelper.NonLibraryProjectOnlyFilter;
     23 import com.android.sdklib.xml.ManifestData;
     24 import com.android.sdklib.xml.ManifestData.Activity;
     25 
     26 import org.eclipse.core.resources.IProject;
     27 import org.eclipse.core.resources.IResource;
     28 import org.eclipse.core.resources.IWorkspaceRoot;
     29 import org.eclipse.core.resources.ResourcesPlugin;
     30 import org.eclipse.core.runtime.CoreException;
     31 import org.eclipse.debug.core.ILaunchConfiguration;
     32 import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
     33 import org.eclipse.debug.ui.AbstractLaunchConfigurationTab;
     34 import org.eclipse.debug.ui.ILaunchConfigurationTab;
     35 import org.eclipse.jdt.core.IJavaModel;
     36 import org.eclipse.jdt.core.IJavaProject;
     37 import org.eclipse.jdt.core.JavaCore;
     38 import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
     39 import org.eclipse.swt.SWT;
     40 import org.eclipse.swt.events.ModifyEvent;
     41 import org.eclipse.swt.events.ModifyListener;
     42 import org.eclipse.swt.events.SelectionAdapter;
     43 import org.eclipse.swt.events.SelectionEvent;
     44 import org.eclipse.swt.events.SelectionListener;
     45 import org.eclipse.swt.graphics.Font;
     46 import org.eclipse.swt.graphics.Image;
     47 import org.eclipse.swt.layout.GridData;
     48 import org.eclipse.swt.layout.GridLayout;
     49 import org.eclipse.swt.widgets.Button;
     50 import org.eclipse.swt.widgets.Combo;
     51 import org.eclipse.swt.widgets.Composite;
     52 import org.eclipse.swt.widgets.Group;
     53 import org.eclipse.swt.widgets.Text;
     54 
     55 import java.util.ArrayList;
     56 
     57 /**
     58  * Class for the main launch configuration tab.
     59  */
     60 public class MainLaunchConfigTab extends AbstractLaunchConfigurationTab {
     61 
     62     /**
     63      *
     64      */
     65     public static final String LAUNCH_TAB_IMAGE = "mainLaunchTab"; //$NON-NLS-1$
     66 
     67     protected static final String EMPTY_STRING = ""; //$NON-NLS-1$
     68 
     69     protected Text mProjText;
     70     private Button mProjButton;
     71 
     72     private Combo mActivityCombo;
     73     private final ArrayList<Activity> mActivities = new ArrayList<Activity>();
     74 
     75     private WidgetListener mListener = new WidgetListener();
     76 
     77     private Button mDefaultActionButton;
     78     private Button mActivityActionButton;
     79     private Button mDoNothingActionButton;
     80     private int mLaunchAction = LaunchConfigDelegate.DEFAULT_LAUNCH_ACTION;
     81 
     82     private ProjectChooserHelper mProjectChooserHelper;
     83 
     84     /**
     85      * A listener which handles widget change events for the controls in this
     86      * tab.
     87      */
     88     private class WidgetListener implements ModifyListener, SelectionListener {
     89 
     90         public void modifyText(ModifyEvent e) {
     91             IProject project = checkParameters();
     92             loadActivities(project);
     93             setDirty(true);
     94         }
     95 
     96         public void widgetDefaultSelected(SelectionEvent e) {/* do nothing */
     97         }
     98 
     99         public void widgetSelected(SelectionEvent e) {
    100             Object source = e.getSource();
    101             if (source == mProjButton) {
    102                 handleProjectButtonSelected();
    103             } else {
    104                 checkParameters();
    105             }
    106         }
    107     }
    108 
    109     public MainLaunchConfigTab() {
    110     }
    111 
    112     public void createControl(Composite parent) {
    113         mProjectChooserHelper = new ProjectChooserHelper(parent.getShell(),
    114                 new NonLibraryProjectOnlyFilter());
    115 
    116         Font font = parent.getFont();
    117         Composite comp = new Composite(parent, SWT.NONE);
    118         setControl(comp);
    119         GridLayout topLayout = new GridLayout();
    120         topLayout.verticalSpacing = 0;
    121         comp.setLayout(topLayout);
    122         comp.setFont(font);
    123         createProjectEditor(comp);
    124         createVerticalSpacer(comp, 1);
    125 
    126         // create the combo for the activity chooser
    127         Group group = new Group(comp, SWT.NONE);
    128         group.setText("Launch Action:");
    129         GridData gd = new GridData(GridData.FILL_HORIZONTAL);
    130         group.setLayoutData(gd);
    131         GridLayout layout = new GridLayout();
    132         layout.numColumns = 2;
    133         group.setLayout(layout);
    134         group.setFont(font);
    135 
    136         mDefaultActionButton = new Button(group, SWT.RADIO);
    137         gd = new GridData(GridData.FILL_HORIZONTAL);
    138         gd.horizontalSpan = 2;
    139         mDefaultActionButton.setLayoutData(gd);
    140         mDefaultActionButton.setText("Launch Default Activity");
    141         mDefaultActionButton.addSelectionListener(new SelectionAdapter() {
    142             @Override
    143             public void widgetSelected(SelectionEvent e) {
    144                 // event are received for both selection and deselection, so we only process
    145                 // the selection event to avoid doing it twice.
    146                 if (mDefaultActionButton.getSelection() == true) {
    147                     mLaunchAction = LaunchConfigDelegate.ACTION_DEFAULT;
    148                     mActivityCombo.setEnabled(false);
    149                     checkParameters();
    150                 }
    151             }
    152         });
    153 
    154         mActivityActionButton = new Button(group, SWT.RADIO);
    155         mActivityActionButton.setText("Launch:");
    156         mActivityActionButton.addSelectionListener(new SelectionAdapter() {
    157             @Override
    158             public void widgetSelected(SelectionEvent e) {
    159                 // event are received for both selection and deselection, so we only process
    160                 // the selection event to avoid doing it twice.
    161                 if (mActivityActionButton.getSelection() == true) {
    162                     mLaunchAction = LaunchConfigDelegate.ACTION_ACTIVITY;
    163                     mActivityCombo.setEnabled(true);
    164                     checkParameters();
    165                 }
    166             }
    167         });
    168 
    169         mActivityCombo = new Combo(group, SWT.DROP_DOWN | SWT.READ_ONLY);
    170         gd = new GridData(GridData.FILL_HORIZONTAL);
    171         mActivityCombo.setLayoutData(gd);
    172         mActivityCombo.clearSelection();
    173         mActivityCombo.setEnabled(false);
    174         mActivityCombo.addSelectionListener(new SelectionAdapter() {
    175             @Override
    176             public void widgetSelected(SelectionEvent e) {
    177                 checkParameters();
    178             }
    179         });
    180 
    181         mDoNothingActionButton = new Button(group, SWT.RADIO);
    182         gd = new GridData(GridData.FILL_HORIZONTAL);
    183         gd.horizontalSpan = 2;
    184         mDoNothingActionButton.setLayoutData(gd);
    185         mDoNothingActionButton.setText("Do Nothing");
    186         mDoNothingActionButton.addSelectionListener(new SelectionAdapter() {
    187             @Override
    188             public void widgetSelected(SelectionEvent e) {
    189                 // event are received for both selection and deselection, so we only process
    190                 // the selection event to avoid doing it twice.
    191                 if (mDoNothingActionButton.getSelection() == true) {
    192                     mLaunchAction = LaunchConfigDelegate.ACTION_DO_NOTHING;
    193                     mActivityCombo.setEnabled(false);
    194                     checkParameters();
    195                 }
    196             }
    197         });
    198 
    199     }
    200 
    201     public String getName() {
    202         return "Android";
    203     }
    204 
    205     @Override
    206     public Image getImage() {
    207         return IconFactory.getInstance().getIcon(LAUNCH_TAB_IMAGE);
    208     }
    209 
    210     public void performApply(ILaunchConfigurationWorkingCopy configuration) {
    211         configuration.setAttribute(
    212                 IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, mProjText.getText());
    213         configuration.setAttribute(
    214                 IJavaLaunchConfigurationConstants.ATTR_ALLOW_TERMINATE, true);
    215 
    216         // add the launch mode
    217         configuration.setAttribute(LaunchConfigDelegate.ATTR_LAUNCH_ACTION, mLaunchAction);
    218 
    219         // add the activity
    220         int selection = mActivityCombo.getSelectionIndex();
    221         if (mActivities != null && selection >=0 && selection < mActivities.size()) {
    222             configuration.setAttribute(LaunchConfigDelegate.ATTR_ACTIVITY,
    223                     mActivities.get(selection).getName());
    224         }
    225 
    226         // link the project and the launch config.
    227         mapResources(configuration);
    228     }
    229 
    230     public void setDefaults(ILaunchConfigurationWorkingCopy configuration) {
    231         configuration.setAttribute(LaunchConfigDelegate.ATTR_LAUNCH_ACTION,
    232                 LaunchConfigDelegate.DEFAULT_LAUNCH_ACTION);
    233     }
    234 
    235     /**
    236      * Creates the widgets for specifying a main type.
    237      *
    238      * @param parent the parent composite
    239      */
    240     protected void createProjectEditor(Composite parent) {
    241         Font font = parent.getFont();
    242         Group group = new Group(parent, SWT.NONE);
    243         group.setText("Project:");
    244         GridData gd = new GridData(GridData.FILL_HORIZONTAL);
    245         group.setLayoutData(gd);
    246         GridLayout layout = new GridLayout();
    247         layout.numColumns = 2;
    248         group.setLayout(layout);
    249         group.setFont(font);
    250         mProjText = new Text(group, SWT.SINGLE | SWT.BORDER);
    251         gd = new GridData(GridData.FILL_HORIZONTAL);
    252         mProjText.setLayoutData(gd);
    253         mProjText.setFont(font);
    254         mProjText.addModifyListener(mListener);
    255         mProjButton = createPushButton(group, "Browse...", null);
    256         mProjButton.addSelectionListener(mListener);
    257     }
    258 
    259     /**
    260      * returns the default listener from this class. For all subclasses this
    261      * listener will only provide the functi Jaonality of updating the current
    262      * tab
    263      *
    264      * @return a widget listener
    265      */
    266     protected WidgetListener getDefaultListener() {
    267         return mListener;
    268     }
    269 
    270     /**
    271      * Return the {@link IJavaProject} corresponding to the project name in the project
    272      * name text field, or null if the text does not match a project name.
    273      * @param javaModel the Java Model object corresponding for the current workspace root.
    274      * @return a IJavaProject object or null.
    275      */
    276     protected IJavaProject getJavaProject(IJavaModel javaModel) {
    277         String projectName = mProjText.getText().trim();
    278         if (projectName.length() < 1) {
    279             return null;
    280         }
    281         return javaModel.getJavaProject(projectName);
    282     }
    283 
    284     /**
    285      * Show a dialog that lets the user select a project. This in turn provides
    286      * context for the main type, allowing the user to key a main type name, or
    287      * constraining the search for main types to the specified project.
    288      */
    289     protected void handleProjectButtonSelected() {
    290         IJavaProject javaProject = mProjectChooserHelper.chooseJavaProject(
    291                 mProjText.getText().trim(),
    292                 "Please select a project to launch");
    293         if (javaProject == null) {
    294             return;
    295         }// end if
    296         String projectName = javaProject.getElementName();
    297         mProjText.setText(projectName);
    298 
    299         // get the list of activities and fill the combo
    300         IProject project = javaProject.getProject();
    301         loadActivities(project);
    302     }// end handle selected
    303 
    304     /**
    305      * Initializes this tab's controls with values from the given
    306      * launch configuration. This method is called when
    307      * a configuration is selected to view or edit, after this
    308      * tab's control has been created.
    309      *
    310      * @param config launch configuration
    311      *
    312      * @see ILaunchConfigurationTab
    313      */
    314     public void initializeFrom(ILaunchConfiguration config) {
    315         String projectName = EMPTY_STRING;
    316         try {
    317             projectName = config.getAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME,
    318                     EMPTY_STRING);
    319         }// end try
    320         catch (CoreException ce) {
    321         }
    322         mProjText.setText(projectName);
    323 
    324         IProject proj = mProjectChooserHelper.getAndroidProject(projectName);
    325         loadActivities(proj);
    326 
    327         // load the launch action.
    328         mLaunchAction = LaunchConfigDelegate.DEFAULT_LAUNCH_ACTION;
    329         try {
    330             mLaunchAction = config.getAttribute(LaunchConfigDelegate.ATTR_LAUNCH_ACTION,
    331                     mLaunchAction);
    332         } catch (CoreException e) {
    333             // nothing to be done really. launchAction will keep its default value.
    334         }
    335 
    336         mDefaultActionButton.setSelection(mLaunchAction == LaunchConfigDelegate.ACTION_DEFAULT);
    337         mActivityActionButton.setSelection(mLaunchAction == LaunchConfigDelegate.ACTION_ACTIVITY);
    338         mDoNothingActionButton.setSelection(
    339                 mLaunchAction == LaunchConfigDelegate.ACTION_DO_NOTHING);
    340 
    341         // now look for the activity and load it if present, otherwise, revert
    342         // to the current one.
    343         String activityName = EMPTY_STRING;
    344         try {
    345             activityName = config.getAttribute(LaunchConfigDelegate.ATTR_ACTIVITY, EMPTY_STRING);
    346         }// end try
    347         catch (CoreException ce) {
    348             // nothing to be done really. activityName will stay empty
    349         }
    350 
    351         if (mLaunchAction != LaunchConfigDelegate.ACTION_ACTIVITY) {
    352             mActivityCombo.setEnabled(false);
    353             mActivityCombo.clearSelection();
    354         } else {
    355             mActivityCombo.setEnabled(true);
    356             if (activityName == null || activityName.equals(EMPTY_STRING)) {
    357                 mActivityCombo.clearSelection();
    358             } else if (mActivities != null && mActivities.size() > 0) {
    359                 // look for the name of the activity in the combo.
    360                 boolean found = false;
    361                 for (int i = 0 ; i < mActivities.size() ; i++) {
    362                     if (activityName.equals(mActivities.get(i).getName())) {
    363                         found = true;
    364                         mActivityCombo.select(i);
    365                         break;
    366                     }
    367                 }
    368 
    369                 // if we haven't found a matching activity we clear the combo selection
    370                 if (found == false) {
    371                     mActivityCombo.clearSelection();
    372                 }
    373             }
    374         }
    375     }
    376 
    377     /**
    378      * Associates the launch config and the project. This allows Eclipse to delete the launch
    379      * config when the project is deleted.
    380      *
    381      * @param config the launch config working copy.
    382      */
    383     protected void mapResources(ILaunchConfigurationWorkingCopy config) {
    384         // get the java model
    385         IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
    386         IJavaModel javaModel = JavaCore.create(workspaceRoot);
    387 
    388         // get the IJavaProject described by the text field.
    389         IJavaProject javaProject = getJavaProject(javaModel);
    390         IResource[] resources = null;
    391         if (javaProject != null) {
    392             resources = AndroidLaunchController.getResourcesToMap(javaProject.getProject());
    393         }
    394         config.setMappedResources(resources);
    395     }
    396 
    397     /**
    398      * Loads the ui with the activities of the specified project, and stores the
    399      * activities in <code>mActivities</code>.
    400      * <p/>
    401      * First activity is selected by default if present.
    402      *
    403      * @param project The project to load the activities from.
    404      */
    405     private void loadActivities(IProject project) {
    406         if (project != null) {
    407             // parse the manifest for the list of activities.
    408             ManifestData manifestData = AndroidManifestHelper.parseForData(project);
    409             if (manifestData != null) {
    410                 Activity[] activities = manifestData.getActivities();
    411 
    412                 mActivities.clear();
    413                 mActivityCombo.removeAll();
    414 
    415                 for (Activity activity : activities) {
    416                     if (activity.isExported() && activity.hasAction()) {
    417                         mActivities.add(activity);
    418                         mActivityCombo.add(activity.getName());
    419                     }
    420                 }
    421 
    422                 if (mActivities.size() > 0) {
    423                     if (mLaunchAction == LaunchConfigDelegate.ACTION_ACTIVITY) {
    424                         mActivityCombo.setEnabled(true);
    425                     }
    426                 } else {
    427                     mActivityCombo.setEnabled(false);
    428                 }
    429 
    430                 // the selection will be set when we update the ui from the current
    431                 // config object.
    432                 mActivityCombo.clearSelection();
    433 
    434                 return;
    435             }
    436         }
    437 
    438         // if we reach this point, either project is null, or we got an exception during
    439         // the parsing. In either case, we empty the activity list.
    440         mActivityCombo.removeAll();
    441         mActivities.clear();
    442     }
    443 
    444     /**
    445      * Checks the parameters for correctness, and update the error message and buttons.
    446      * @return the current IProject of this launch config.
    447      */
    448     private IProject checkParameters() {
    449         try {
    450             //test the project name first!
    451             String text = mProjText.getText();
    452             if (text.length() == 0) {
    453                 setErrorMessage("Project Name is required!");
    454             } else if (text.matches("[a-zA-Z0-9_ \\.-]+") == false) {
    455                 setErrorMessage("Project name contains unsupported characters!");
    456             } else {
    457                 IJavaProject[] projects = mProjectChooserHelper.getAndroidProjects(null);
    458                 IProject found = null;
    459                 for (IJavaProject javaProject : projects) {
    460                     if (javaProject.getProject().getName().equals(text)) {
    461                         found = javaProject.getProject();
    462                         break;
    463                     }
    464 
    465                 }
    466 
    467                 if (found != null) {
    468                     setErrorMessage(null);
    469                 } else {
    470                     setErrorMessage(String.format("There is no android project named '%1$s'",
    471                             text));
    472                 }
    473 
    474                 return found;
    475             }
    476         } finally {
    477             updateLaunchConfigurationDialog();
    478         }
    479 
    480         return null;
    481     }
    482 }
    483