• 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.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 
modifyText(ModifyEvent e)90         public void modifyText(ModifyEvent e) {
91             IProject project = checkParameters();
92             loadActivities(project);
93             setDirty(true);
94         }
95 
widgetDefaultSelected(SelectionEvent e)96         public void widgetDefaultSelected(SelectionEvent e) {/* do nothing */
97         }
98 
widgetSelected(SelectionEvent e)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 
MainLaunchConfigTab()109     public MainLaunchConfigTab() {
110     }
111 
createControl(Composite parent)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 
getName()201     public String getName() {
202         return "Android";
203     }
204 
205     @Override
getImage()206     public Image getImage() {
207         return IconFactory.getInstance().getIcon(LAUNCH_TAB_IMAGE);
208     }
209 
performApply(ILaunchConfigurationWorkingCopy configuration)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 
setDefaults(ILaunchConfigurationWorkingCopy configuration)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      */
createProjectEditor(Composite parent)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      */
getDefaultListener()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      */
getJavaProject(IJavaModel javaModel)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      */
handleProjectButtonSelected()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      */
initializeFrom(ILaunchConfiguration config)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      */
mapResources(ILaunchConfigurationWorkingCopy config)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      */
loadActivities(IProject project)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      */
checkParameters()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