1 /* 2 * Copyright (C) 2011 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 package com.android.ide.eclipse.adt.internal.wizards.newproject; 17 18 import com.android.ide.common.xml.ManifestData; 19 import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper; 20 import com.android.ide.eclipse.adt.internal.project.ProjectChooserHelper; 21 import com.android.ide.eclipse.adt.internal.sdk.Sdk; 22 import com.android.sdklib.IAndroidTarget; 23 24 import org.eclipse.core.resources.IProject; 25 import org.eclipse.core.resources.IWorkspaceRoot; 26 import org.eclipse.core.resources.ResourcesPlugin; 27 import org.eclipse.jdt.core.IJavaModel; 28 import org.eclipse.jdt.core.IJavaProject; 29 import org.eclipse.jdt.core.JavaCore; 30 import org.eclipse.jdt.ui.JavaElementLabelProvider; 31 import org.eclipse.jface.dialogs.IMessageProvider; 32 import org.eclipse.jface.viewers.ILabelProvider; 33 import org.eclipse.jface.wizard.WizardPage; 34 import org.eclipse.swt.SWT; 35 import org.eclipse.swt.events.SelectionEvent; 36 import org.eclipse.swt.events.SelectionListener; 37 import org.eclipse.swt.layout.GridData; 38 import org.eclipse.swt.layout.GridLayout; 39 import org.eclipse.swt.widgets.Button; 40 import org.eclipse.swt.widgets.Composite; 41 import org.eclipse.ui.dialogs.FilteredList; 42 43 /** 44 * Page shown when creating a test project which lets you choose between testing 45 * yourself and testing a different project 46 */ 47 class TestTargetPage extends WizardPage implements SelectionListener { 48 private final NewProjectWizardState mValues; 49 /** Flag used when setting button/text state manually to ignore listener updates */ 50 private boolean mIgnore; 51 private String mLastExistingPackageName; 52 53 private Button mCurrentRadioButton; 54 private Button mExistingRadioButton; 55 private FilteredList mProjectList; 56 private boolean mPageShown; 57 58 /** 59 * Create the wizard. 60 */ 61 TestTargetPage(NewProjectWizardState values) { 62 super("testTargetPage"); //$NON-NLS-1$ 63 setTitle("Select Test Target"); 64 setDescription("Choose a project to test"); 65 mValues = values; 66 } 67 68 /** 69 * Create contents of the wizard. 70 */ 71 @Override 72 public void createControl(Composite parent) { 73 Composite container = new Composite(parent, SWT.NULL); 74 75 setControl(container); 76 container.setLayout(new GridLayout(2, false)); 77 78 mCurrentRadioButton = new Button(container, SWT.RADIO); 79 mCurrentRadioButton.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1)); 80 mCurrentRadioButton.setText("This project"); 81 mCurrentRadioButton.addSelectionListener(this); 82 83 mExistingRadioButton = new Button(container, SWT.RADIO); 84 mExistingRadioButton.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1)); 85 mExistingRadioButton.setText("An existing Android project:"); 86 mExistingRadioButton.addSelectionListener(this); 87 88 ILabelProvider labelProvider = new JavaElementLabelProvider( 89 JavaElementLabelProvider.SHOW_DEFAULT); 90 mProjectList = new FilteredList(container, 91 SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL | SWT.SINGLE, labelProvider, 92 true /*ignoreCase*/, false /*allowDuplicates*/, true /* matchEmptyString*/); 93 mProjectList.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1)); 94 mProjectList.addSelectionListener(this); 95 } 96 97 private void initializeList() { 98 ProjectChooserHelper helper = new ProjectChooserHelper(getShell(), null /*filter*/); 99 IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); 100 IJavaModel javaModel = JavaCore.create(workspaceRoot); 101 IJavaProject[] androidProjects = helper.getAndroidProjects(javaModel); 102 mProjectList.setElements(androidProjects); 103 if (mValues.testedProject != null) { 104 for (IJavaProject project : androidProjects) { 105 if (project.getProject() == mValues.testedProject) { 106 mProjectList.setSelection(new Object[] { project }); 107 break; 108 } 109 } 110 } else { 111 // No initial selection: force the user to choose 112 mProjectList.setSelection(new int[0]); 113 } 114 } 115 116 @Override 117 public void setVisible(boolean visible) { 118 super.setVisible(visible); 119 mPageShown = true; 120 121 if (visible) { 122 try { 123 mIgnore = true; 124 mCurrentRadioButton.setSelection(mValues.testingSelf); 125 mExistingRadioButton.setSelection(!mValues.testingSelf); 126 mProjectList.setEnabled(!mValues.testingSelf); 127 128 if (mProjectList.isEmpty()) { 129 initializeList(); 130 } 131 if (!mValues.testingSelf) { 132 mProjectList.setFocus(); 133 IProject project = getSelectedProject(); 134 if (project != null) { 135 // The FilteredList seems to -insist- on selecting the first item 136 // in the list, even when the selection is explicitly set to an empty 137 // array. This means the user is looking at a selection, so we need 138 // to also go ahead and select this item in the model such that the 139 // two agree, even if we would have preferred to have no initial 140 // selection. 141 mValues.testedProject = project; 142 } 143 } 144 } finally { 145 mIgnore = false; 146 } 147 } 148 149 validatePage(); 150 } 151 152 @Override 153 public void widgetSelected(SelectionEvent e) { 154 if (mIgnore) { 155 return; 156 } 157 158 Object source = e.getSource(); 159 if (source == mExistingRadioButton) { 160 mProjectList.setEnabled(true); 161 mValues.testingSelf = false; 162 setExistingProject(getSelectedProject()); 163 mProjectList.setFocus(); 164 } else if (source == mCurrentRadioButton) { 165 mProjectList.setEnabled(false); 166 mValues.testingSelf = true; 167 mValues.testedProject = null; 168 } else { 169 // The event must be from the project list, which unfortunately doesn't 170 // pass itself as the selection event, it passes a reference to some internal 171 // table widget that it uses, so we check for this case last 172 IProject project = getSelectedProject(); 173 if (project != mValues.testedProject) { 174 setExistingProject(project); 175 } 176 } 177 178 validatePage(); 179 } 180 181 private IProject getSelectedProject() { 182 Object[] selection = mProjectList.getSelection(); 183 IProject project = selection != null && selection.length == 1 184 ? ((IJavaProject) selection[0]).getProject() : null; 185 return project; 186 } 187 188 @Override 189 public void widgetDefaultSelected(SelectionEvent e) { 190 } 191 192 private void setExistingProject(IProject project) { 193 mValues.testedProject = project; 194 195 // Try to update the application, package, sdk target and minSdkVersion accordingly 196 if (project != null && 197 (!mValues.applicationNameModifiedByUser || 198 !mValues.packageNameModifiedByUser || 199 !mValues.targetModifiedByUser || 200 !mValues.minSdkModifiedByUser)) { 201 ManifestData manifestData = AndroidManifestHelper.parseForData(project); 202 if (manifestData != null) { 203 String appName = String.format("%1$sTest", project.getName()); 204 String packageName = manifestData.getPackage(); 205 String minSdkVersion = manifestData.getMinSdkVersionString(); 206 IAndroidTarget sdkTarget = null; 207 if (Sdk.getCurrent() != null) { 208 sdkTarget = Sdk.getCurrent().getTarget(project); 209 } 210 211 if (packageName == null) { 212 packageName = ""; //$NON-NLS-1$ 213 } 214 mLastExistingPackageName = packageName; 215 216 if (!mValues.projectNameModifiedByUser) { 217 mValues.projectName = appName; 218 } 219 220 if (!mValues.applicationNameModifiedByUser) { 221 mValues.applicationName = appName; 222 } 223 224 if (!mValues.packageNameModifiedByUser) { 225 packageName += ".test"; //$NON-NLS-1$ 226 mValues.packageName = packageName; 227 } 228 229 if (!mValues.targetModifiedByUser && sdkTarget != null) { 230 mValues.target = sdkTarget; 231 } 232 233 if (!mValues.minSdkModifiedByUser) { 234 if (minSdkVersion != null || sdkTarget != null) { 235 mValues.minSdk = minSdkVersion; 236 } 237 if (sdkTarget == null) { 238 mValues.updateSdkTargetToMatchMinSdkVersion(); 239 } 240 } 241 } 242 } 243 244 updateTestTargetPackageField(mLastExistingPackageName); 245 } 246 247 /** 248 * Updates the test target package name 249 * 250 * When using the "self-test" option, the packageName argument is ignored and the 251 * current value from the project package is used. 252 * 253 * Otherwise the packageName is used if it is not null. 254 */ 255 private void updateTestTargetPackageField(String packageName) { 256 if (mValues.testingSelf) { 257 mValues.testTargetPackageName = mValues.packageName; 258 } else if (packageName != null) { 259 mValues.testTargetPackageName = packageName; 260 } 261 } 262 263 @Override 264 public boolean isPageComplete() { 265 // Ensure that the user sees the page and makes a selection 266 if (!mPageShown) { 267 return false; 268 } 269 270 return super.isPageComplete(); 271 } 272 273 private void validatePage() { 274 String error = null; 275 276 if (!mValues.testingSelf) { 277 if (mValues.testedProject == null) { 278 error = "Please select an existing Android project as a test target."; 279 } else if (mValues.projectName.equals(mValues.testedProject.getName())) { 280 error = "The main project name and the test project name must be different."; 281 } 282 } 283 284 // -- update UI & enable finish if there's no error 285 setPageComplete(error == null); 286 if (error != null) { 287 setMessage(error, IMessageProvider.ERROR); 288 } else { 289 setErrorMessage(null); 290 setMessage(null); 291 } 292 } 293 } 294