/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.motorola.studio.android.wizards.project; import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceDescription; import org.eclipse.core.resources.IncrementalProjectBuilder; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.QualifiedName; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.wizard.WizardPage; import org.eclipse.osgi.util.NLS; import org.eclipse.ui.INewWizard; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.wizards.newresource.BasicNewProjectResourceWizard; import org.osgi.framework.Bundle; import com.android.sdklib.IAndroidTarget; import com.motorola.studio.android.AndroidPlugin; import com.motorola.studio.android.common.log.StudioLogger; import com.motorola.studio.android.common.preferences.DialogWithToggleUtils; import com.motorola.studio.android.common.utilities.EclipseUtils; import com.motorola.studio.android.i18n.AndroidNLS; import com.motorola.studio.android.model.AndroidProject; import com.motorola.studio.android.obfuscate.ObfuscatorManager; /** * Class that represents the Android New Project Wizard */ public class NewAndroidProjectWizard extends BasicNewProjectResourceWizard implements INewWizard { private static final String WIZARD_BANNER = "icons/wizban/newprjwiz.png"; //$NON-NLS-1$ protected static final String NATIVE_PAGE_NAME = "native_page"; //$NON-NLS-1$ protected static final String SAMPLE_PAGE_NAME = AndroidNLS.UI_SampleSelectionPage_TitleSourcePage; private final AndroidProject project = new AndroidProject(); private WizardPage nativePage = null; private NewAndroidProjectMainPage mainPage = null; private Class<?> nativePageClass = null; private Object classInstance = null; /* (non-Javadoc) * @see org.eclipse.jface.wizard.Wizard#canFinish() */ @Override public boolean canFinish() { boolean canFinish = (project.getStatus().getSeverity() != IStatus.ERROR) && !project.needMoreInformation(); if ((nativePage != null) && !nativePage.isPageComplete() && project.isAddingNativeSupport()) { canFinish = false; } return canFinish; } /* (non-Javadoc) * @see org.eclipse.ui.wizards.newresource.BasicNewProjectResourceWizard#performFinish() */ @Override public boolean performFinish() { DoSave doSave = new DoSave(); try { getContainer().run(false, false, doSave); } catch (Exception e) { String errMsg = NLS.bind( AndroidNLS.EXC_NewAndroidProjectWizard_AnErrorHasOccurredWhenCreatingTheProject, e.getLocalizedMessage()); StudioLogger.error(NewAndroidProjectWizard.class, errMsg, e); EclipseUtils.showErrorDialog(AndroidNLS.UI_GenericErrorDialogTitle, errMsg, null); } boolean success = doSave.isSaved(); if (success) { // Collecting usage data for statistical purposes try { StudioLogger.collectUsageData(StudioLogger.WHAT_APP_MANAGEMENT_CREATE, StudioLogger.KIND_APP_MANAGEMENT, this.project.getSourceType().name() .toLowerCase(), AndroidPlugin.PLUGIN_ID, AndroidPlugin.getDefault() .getBundle().getVersion().toString()); } catch (Throwable e) { //Do nothing, but error on the log should never prevent app from working } } return success; } /* (non-Javadoc) * @see org.eclipse.jface.wizard.Wizard#performCancel() */ @Override public boolean performCancel() { try { project.finalize(); } catch (Throwable e) { StudioLogger.error(NewAndroidProjectWizard.class, e.getLocalizedMessage(), e); } return super.performCancel(); } /* (non-Javadoc) * @see org.eclipse.ui.wizards.newresource.BasicNewProjectResourceWizard#addPages() */ @SuppressWarnings( { "unchecked" }) @Override public void addPages() { classInstance = null; try { Bundle seqBundle = Platform.getBundle("org.eclipse.sequoyah.android.cdt.build.ui"); //$NON-NLS-1$ if (seqBundle != null) { nativePageClass = seqBundle .loadClass("org.eclipse.sequoyah.android.cdt.internal.build.ui.AddNativeProjectPage"); //$NON-NLS-1$ Class<Boolean> bClass = boolean.class; Class[] paramTypes = { bClass }; Constructor cs = nativePageClass.getConstructor(paramTypes); classInstance = cs.newInstance(true); nativePage = (WizardPage) classInstance; } } catch (Exception e) { StudioLogger.error(NewAndroidProjectWizard.class, e.getMessage(), e); } if (nativePage != null) { mainPage = new NewAndroidProjectMainPage(project, true); addPage(mainPage); addPage(nativePage); } else { mainPage = new NewAndroidProjectMainPage(project, false); addPage(mainPage); } addPage(new SampleSelectionPage(project)); } /* (non-Javadoc) * @see org.eclipse.ui.wizards.newresource.BasicNewProjectResourceWizard#init(org.eclipse.ui.IWorkbench, org.eclipse.jface.viewers.IStructuredSelection) */ @Override public void init(IWorkbench workbench, IStructuredSelection selection) { setWindowTitle(AndroidNLS.UI_NewAndroidProjectWizard_TitleNewProjectWizard); setNeedsProgressMonitor(true); setDefaultPageImageDescriptor(AndroidPlugin.getImageDescriptor(WIZARD_BANNER)); } /** * Implements an IRunnableWithProgress to run the save process */ private class DoSave implements IRunnableWithProgress { private static final String OPHONE_JAR = "oms.jar"; //$NON-NLS-1$ private static final String OPHONESDK_PROMPT_KEY = "OphoneSDK"; //$NON-NLS-1$ private boolean saved = false; /** * Returns whether the project was saved/created successfuly. * * @return Returns <code>true</code> in case the project is saved/creates successfully. */ public boolean isSaved() { return saved; } public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { SubMonitor subMonitor = SubMonitor.convert(monitor, 20); subMonitor.beginTask(AndroidNLS.NewAndroidProjectWizard_Message_CreatingAndroidProject, 10); // Gets the auto-building configuration to set it back in the end final boolean autoBuild = ResourcesPlugin.getWorkspace().isAutoBuilding(); final IWorkspaceDescription wsd = ResourcesPlugin.getWorkspace().getDescription(); wsd.setAutoBuilding(false); try { // Set auto-build off for performance reasons ResourcesPlugin.getWorkspace().setDescription(wsd); } catch (CoreException e) { // there is no need to stop the process because auto-build only improves performance, it does not interferes with the new project creation. StudioLogger.error(NewAndroidProjectWizard.class, "Error cleaning workspace after project creation: " + e.getMessage()); //$NON-NLS-1$ } // worked 1 subMonitor.worked(1); saved = project.save(getContainer(), subMonitor); updatePerspective(); IProject newProject = ResourcesPlugin.getWorkspace().getRoot().getProject(project.getName()); addOphoneSDK(newProject, subMonitor); try { newProject.build(IncrementalProjectBuilder.CLEAN_BUILD, subMonitor); } catch (Exception e1) { // even if the build fais, the project could still be created, therefore it must continue StudioLogger.error(NewAndroidProjectWizard.class, "Sleep error when cleaning workspace after project creation: " //$NON-NLS-1$ + e1.getMessage()); } // worked 4 subMonitor.worked(3); wsd.setAutoBuilding(autoBuild); try { // rollback the auto-building setting to the original state ResourcesPlugin.getWorkspace().setDescription(wsd); } catch (CoreException e) { // the auto-building does not interfere with the project creation, therefore in case it fails, the process may contine StudioLogger.error(NewAndroidProjectWizard.class, "Error cleaning workspace after project creation: " + e.getMessage()); //$NON-NLS-1$ } // worked 5 subMonitor.worked(1); if ((nativePage != null) && project.isAddingNativeSupport() && saved) { try { Class<IWorkbenchWindow> workbenchClass = IWorkbenchWindow.class; Class<IProject> projectClass = IProject.class; Class<IProgressMonitor> progressMonitorClass = IProgressMonitor.class; IProject createdProject = ResourcesPlugin.getWorkspace().getRoot().getProject(project.getName()); IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); QualifiedName libQN = new QualifiedName(AndroidPlugin.PLUGIN_ID, AndroidPlugin.LIB_LOCATION_PROPERTY); //get library name field from wizard Method getLibraryName = nativePageClass.getMethod("getLibraryName", (Class[]) null); //$NON-NLS-1$ getLibraryName.setAccessible(true); Object returnValue = getLibraryName.invoke(classInstance, (Object[]) null); //set project library name property createdProject.setPersistentProperty(libQN, returnValue.toString()); // worked 6 subMonitor.worked(1); Object[] performFinishMethodArguments = { window, createdProject, subMonitor.newChild(4) }; Class<?>[] performFinishMethodParameterTypes = { workbenchClass, projectClass, progressMonitorClass }; //invoke page perform finish that will add native support to the brand new project Method performFinish = nativePageClass.getMethod("performFinish", //$NON-NLS-1$ performFinishMethodParameterTypes); performFinish.setAccessible(true); returnValue = performFinish.invoke(classInstance, performFinishMethodArguments); //update success flag saved = saved && (Boolean) returnValue; } catch (Exception e) { // the project may be in a inconsistent state - throw an exception saved = false; StudioLogger.error(NewAndroidProjectWizard.class, e.getMessage(), e); throw new InvocationTargetException(e); } } //add proguard file inside project try { if (project.needToObfuscate()) { ObfuscatorManager.obfuscate(newProject, subMonitor.newChild(10)); } newProject.refreshLocal(IResource.DEPTH_INFINITE, subMonitor); } catch (Exception e) { StudioLogger.error(NewAndroidProjectWizard.class, e.getMessage(), e); throw new InvocationTargetException(e); } } private void addOphoneSDK(IProject p, IProgressMonitor monitor) { IAndroidTarget sdkTarget = project.getSdkTarget(); File platformLocation = new File(sdkTarget.getLocation()); File[] listFiles = platformLocation.listFiles(); boolean found = false; int i = 0; File file = null; while (!found && (i < listFiles.length)) { file = listFiles[i]; if (file.getName().equals(OPHONE_JAR)) { found = true; } i++; } if (found) { boolean addClasspath = DialogWithToggleUtils.showQuestion(OPHONESDK_PROMPT_KEY, AndroidNLS.NewAndroidProjectWizard_OPhonePromptTitle, AndroidNLS.NewAndroidProjectWizard_OPhonePromptMessage); if (addClasspath) { IJavaProject javaProject = JavaCore.create(p); if ((javaProject != null) && javaProject.exists()) { try { javaProject.open(monitor); IClasspathEntry[] rawClasspath = javaProject.getRawClasspath(); IClasspathEntry[] newClasspath = new IClasspathEntry[rawClasspath.length + 1]; System.arraycopy(rawClasspath, 0, newClasspath, 0, rawClasspath.length); newClasspath[newClasspath.length - 1] = JavaCore.newLibraryEntry(new Path(file.getAbsolutePath()), null, null); javaProject.setRawClasspath(newClasspath, monitor); } catch (JavaModelException e) { StudioLogger.error(NewAndroidProjectWizard.class, "Error while setting up the oms.jar on the project classpath: " //$NON-NLS-1$ + e.getMessage()); } } } } } } }