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