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