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