1 /* 2 * Copyright (C) 2008 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.wizards.export; 18 19 import com.android.ide.eclipse.adt.AndroidConstants; 20 import com.android.ide.eclipse.adt.internal.editors.IconFactory; 21 import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper; 22 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; 23 import com.android.ide.eclipse.adt.internal.project.ProjectChooserHelper; 24 import com.android.ide.eclipse.adt.internal.project.ProjectHelper; 25 import com.android.ide.eclipse.adt.internal.project.ProjectChooserHelper.NonLibraryProjectOnlyFilter; 26 import com.android.ide.eclipse.adt.internal.wizards.export.ExportWizard.ExportWizardPage; 27 import com.android.sdklib.xml.ManifestData; 28 29 import org.eclipse.core.resources.IFolder; 30 import org.eclipse.core.resources.IProject; 31 import org.eclipse.core.runtime.CoreException; 32 import org.eclipse.jdt.core.IJavaProject; 33 import org.eclipse.swt.SWT; 34 import org.eclipse.swt.events.ModifyEvent; 35 import org.eclipse.swt.events.ModifyListener; 36 import org.eclipse.swt.events.SelectionAdapter; 37 import org.eclipse.swt.events.SelectionEvent; 38 import org.eclipse.swt.graphics.Image; 39 import org.eclipse.swt.layout.GridData; 40 import org.eclipse.swt.layout.GridLayout; 41 import org.eclipse.swt.widgets.Button; 42 import org.eclipse.swt.widgets.Composite; 43 import org.eclipse.swt.widgets.Display; 44 import org.eclipse.swt.widgets.Label; 45 import org.eclipse.swt.widgets.Text; 46 47 import java.io.File; 48 49 /** 50 * First Export Wizard Page. Display warning/errors. 51 */ 52 final class ProjectCheckPage extends ExportWizardPage { 53 private final static String IMG_ERROR = "error.png"; //$NON-NLS-1$ 54 private final static String IMG_WARNING = "warning.png"; //$NON-NLS-1$ 55 56 private final ExportWizard mWizard; 57 private Display mDisplay; 58 private Image mError; 59 private Image mWarning; 60 private boolean mHasMessage = false; 61 private Composite mTopComposite; 62 private Composite mErrorComposite; 63 private Text mProjectText; 64 private ProjectChooserHelper mProjectChooserHelper; 65 private boolean mFirstOnShow = true; 66 67 protected ProjectCheckPage(ExportWizard wizard, String pageName) { 68 super(pageName); 69 mWizard = wizard; 70 71 setTitle("Project Checks"); 72 setDescription("Performs a set of checks to make sure the application can be exported."); 73 } 74 75 public void createControl(Composite parent) { 76 mProjectChooserHelper = new ProjectChooserHelper(parent.getShell(), 77 new NonLibraryProjectOnlyFilter()); 78 mDisplay = parent.getDisplay(); 79 80 GridLayout gl = null; 81 GridData gd = null; 82 83 mTopComposite = new Composite(parent, SWT.NONE); 84 mTopComposite.setLayoutData(new GridData(GridData.FILL_BOTH)); 85 mTopComposite.setLayout(new GridLayout(1, false)); 86 87 // composite for the project selection. 88 Composite projectComposite = new Composite(mTopComposite, SWT.NONE); 89 projectComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 90 projectComposite.setLayout(gl = new GridLayout(3, false)); 91 gl.marginHeight = gl.marginWidth = 0; 92 93 Label label = new Label(projectComposite, SWT.NONE); 94 label.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); 95 gd.horizontalSpan = 3; 96 label.setText("Select the project to export:"); 97 98 new Label(projectComposite, SWT.NONE).setText("Project:"); 99 mProjectText = new Text(projectComposite, SWT.BORDER); 100 mProjectText.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); 101 mProjectText.addModifyListener(new ModifyListener() { 102 public void modifyText(ModifyEvent e) { 103 handleProjectNameChange(); 104 } 105 }); 106 107 Button browseButton = new Button(projectComposite, SWT.PUSH); 108 browseButton.setText("Browse..."); 109 browseButton.addSelectionListener(new SelectionAdapter() { 110 @Override 111 public void widgetSelected(SelectionEvent e) { 112 IJavaProject javaProject = mProjectChooserHelper.chooseJavaProject( 113 mProjectText.getText().trim(), 114 "Please select a project to export"); 115 116 if (javaProject != null) { 117 IProject project = javaProject.getProject(); 118 119 // set the new name in the text field. The modify listener will take 120 // care of updating the status and the ExportWizard object. 121 mProjectText.setText(project.getName()); 122 } 123 } 124 }); 125 126 setControl(mTopComposite); 127 } 128 129 @Override 130 void onShow() { 131 if (mFirstOnShow) { 132 // get the project and init the ui 133 IProject project = mWizard.getProject(); 134 if (project != null) { 135 mProjectText.setText(project.getName()); 136 } 137 138 mFirstOnShow = false; 139 } 140 } 141 142 private void buildErrorUi(IProject project) { 143 // Show description the first time 144 setErrorMessage(null); 145 setMessage(null); 146 setPageComplete(true); 147 mHasMessage = false; 148 149 // composite parent for the warning/error 150 GridLayout gl = null; 151 mErrorComposite = new Composite(mTopComposite, SWT.NONE); 152 mErrorComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 153 gl = new GridLayout(2, false); 154 gl.marginHeight = gl.marginWidth = 0; 155 gl.verticalSpacing *= 3; // more spacing than normal. 156 mErrorComposite.setLayout(gl); 157 158 if (project == null) { 159 setErrorMessage("Select project to export."); 160 mHasMessage = true; 161 } else { 162 try { 163 if (project.hasNature(AndroidConstants.NATURE_DEFAULT) == false) { 164 addError(mErrorComposite, "Project is not an Android project."); 165 } else { 166 // check for errors 167 if (ProjectHelper.hasError(project, true)) { 168 addError(mErrorComposite, "Project has compilation error(s)"); 169 } 170 171 // check the project output 172 IFolder outputIFolder = BaseProjectHelper.getOutputFolder(project); 173 if (outputIFolder != null) { 174 String outputOsPath = outputIFolder.getLocation().toOSString(); 175 String apkFilePath = outputOsPath + File.separator + project.getName() + 176 AndroidConstants.DOT_ANDROID_PACKAGE; 177 178 File f = new File(apkFilePath); 179 if (f.isFile() == false) { 180 addError(mErrorComposite, 181 String.format("%1$s/%2$s/%1$s%3$s does not exists!", 182 project.getName(), 183 outputIFolder.getName(), 184 AndroidConstants.DOT_ANDROID_PACKAGE)); 185 } 186 } else { 187 addError(mErrorComposite, 188 "Unable to get the output folder of the project!"); 189 } 190 191 192 // project is an android project, we check the debuggable attribute. 193 ManifestData manifestData = AndroidManifestHelper.parseForData(project); 194 Boolean debuggable = null; 195 if (manifestData != null) { 196 debuggable = manifestData.getDebuggable(); 197 } 198 199 if (debuggable != null && debuggable == Boolean.TRUE) { 200 addWarning(mErrorComposite, 201 "The manifest 'debuggable' attribute is set to true.\nYou should set it to false for applications that you release to the public."); 202 } 203 204 // check for mapview stuff 205 } 206 } catch (CoreException e) { 207 // unable to access nature 208 addError(mErrorComposite, "Unable to get project nature"); 209 } 210 } 211 212 if (mHasMessage == false) { 213 Label label = new Label(mErrorComposite, SWT.NONE); 214 GridData gd = new GridData(GridData.FILL_HORIZONTAL); 215 gd.horizontalSpan = 2; 216 label.setLayoutData(gd); 217 label.setText("No errors found. Click Next."); 218 } 219 220 mTopComposite.layout(); 221 } 222 223 /** 224 * Adds an error label to a {@link Composite} object. 225 * @param parent the Composite parent. 226 * @param message the error message. 227 */ 228 private void addError(Composite parent, String message) { 229 if (mError == null) { 230 mError = IconFactory.getInstance().getIcon(IMG_ERROR); 231 } 232 233 new Label(parent, SWT.NONE).setImage(mError); 234 Label label = new Label(parent, SWT.NONE); 235 label.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 236 label.setText(message); 237 238 setErrorMessage("Application cannot be exported due to the error(s) below."); 239 setPageComplete(false); 240 mHasMessage = true; 241 } 242 243 /** 244 * Adds a warning label to a {@link Composite} object. 245 * @param parent the Composite parent. 246 * @param message the warning message. 247 */ 248 private void addWarning(Composite parent, String message) { 249 if (mWarning == null) { 250 mWarning = IconFactory.getInstance().getIcon(IMG_WARNING); 251 } 252 253 new Label(parent, SWT.NONE).setImage(mWarning); 254 Label label = new Label(parent, SWT.NONE); 255 label.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 256 label.setText(message); 257 258 mHasMessage = true; 259 } 260 261 /** 262 * Checks the parameters for correctness, and update the error message and buttons. 263 */ 264 private void handleProjectNameChange() { 265 setPageComplete(false); 266 267 if (mErrorComposite != null) { 268 mErrorComposite.dispose(); 269 mErrorComposite = null; 270 } 271 272 // update the wizard with the new project 273 mWizard.setProject(null); 274 275 //test the project name first! 276 String text = mProjectText.getText().trim(); 277 if (text.length() == 0) { 278 setErrorMessage("Select project to export."); 279 } else if (text.matches("[a-zA-Z0-9_ \\.-]+") == false) { 280 setErrorMessage("Project name contains unsupported characters!"); 281 } else { 282 IJavaProject[] projects = mProjectChooserHelper.getAndroidProjects(null); 283 IProject found = null; 284 for (IJavaProject javaProject : projects) { 285 if (javaProject.getProject().getName().equals(text)) { 286 found = javaProject.getProject(); 287 break; 288 } 289 290 } 291 292 if (found != null) { 293 setErrorMessage(null); 294 295 // update the wizard with the new project 296 mWizard.setProject(found); 297 298 // now rebuild the error ui. 299 buildErrorUi(found); 300 } else { 301 setErrorMessage(String.format("There is no android project named '%1$s'", 302 text)); 303 } 304 } 305 } 306 } 307