/* * 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.model; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProjectDescription; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceStatus; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.SubProgressMonitor; 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.dialogs.ErrorDialog; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.wizard.IWizardContainer; import org.eclipse.osgi.util.NLS; import org.eclipse.ui.actions.WorkspaceModifyOperation; import com.android.ide.eclipse.adt.AdtPlugin; import com.motorola.studio.android.AndroidPlugin; import com.motorola.studio.android.adt.ProjectUtils; import com.motorola.studio.android.adt.SdkUtils; import com.motorola.studio.android.common.IAndroidConstants; import com.motorola.studio.android.common.exception.AndroidException; import com.motorola.studio.android.common.log.StudioLogger; import com.motorola.studio.android.common.utilities.AndroidStatus; import com.motorola.studio.android.common.utilities.EclipseUtils; import com.motorola.studio.android.common.utilities.FileUtil; import com.motorola.studio.android.i18n.AndroidNLS; import com.motorola.studio.android.model.AndroidProject.SourceTypes; /** * Project Creation Support. */ public class ProjectCreationSupport { /** * Only static calls */ private ProjectCreationSupport() { } private static final String PACKAGE_NAME = "PACKAGE"; //$NON-NLS-1$ private static final String APP_NAME = "app_name"; //$NON-NLS-1$ private static final String APPLICATION_NAME = "APPLICATION_NAME"; //$NON-NLS-1$ private static final String STRING_RSRC_PREFIX = "@string/"; //$NON-NLS-1$ private static final String MIN_SDK_VERSION = "MIN_SDK_VERSION"; //$NON-NLS-1$ private static final String BIN_DIR = IAndroidConstants.FD_OUTPUT + IPath.SEPARATOR; private static final String RES_DIR = IAndroidConstants.FD_RESOURCES + IPath.SEPARATOR; private static final String ASSETS_DIR = IAndroidConstants.FD_ASSETS + IPath.SEPARATOR; private static final String DRAWABLE_DIR = IAndroidConstants.FD_DRAWABLE; private static final String LAYOUT_DIR = IAndroidConstants.FD_LAYOUT + IPath.SEPARATOR; private static final String VALUES_DIR = IAndroidConstants.FD_VALUES + IPath.SEPARATOR; private static final String GEN_DIR = IAndroidConstants.FD_GEN_SOURCES + IPath.SEPARATOR; private static final String XML_DIR = "xml" + IPath.SEPARATOR; private static final String TEMPLATES_DIRECTORY = "templates/"; //$NON-NLS-1$ private static final String MANIFEST_TEMPLATE = TEMPLATES_DIRECTORY + "AndroidManifest.template"; //$NON-NLS-1$ private static final String ACTIVITY_NAME = "ACTIVITY_NAME"; //$NON-NLS-1$ private static final String ACTIVITY_TEMPLATE = TEMPLATES_DIRECTORY + "activity.template"; //$NON-NLS-1$ private static final String LAUNCHER_INTENT_TEMPLATE = TEMPLATES_DIRECTORY + "launcher_intent_filter.template"; //$NON-NLS-1$ private static final String INTENT_FILTERS = "INTENT_FILTERS"; //$NON-NLS-1$ private static final String ACTIVITIES = "ACTIVITIES"; //$NON-NLS-1$ private static final String USES_SDK_TEMPLATE = TEMPLATES_DIRECTORY + "uses-sdk.template"; //$NON-NLS-1$ private static final String USES_SDK = "USES-SDK"; //$NON-NLS-1$ private static final String ICON = "ic_launcher.png"; //$NON-NLS-1$ private static final String JAVA_ACTIVITY_TEMPLATE = "java_file.template"; //$NON-NLS-1$ private static final String MAIN_LAYOUT_XML = "main.xml"; //$NON-NLS-1$ private static final String LAYOUT_TEMPLATE = "layout.template"; //$NON-NLS-1$ private static final String STRING_HELLO_WORLD = "hello"; //$NON-NLS-1$ private static final String TEST_USES_LIBRARY = "TEST-USES-LIBRARY"; //$NON-NLS-1$ private static final String TEST_INSTRUMENTATION = "TEST-INSTRUMENTATION"; //$NON-NLS-1$ private static final String[] DPIS = { "hdpi", "ldpi", "mdpi" }; /* * Widget Project manifest creation constants */ private static final String WIDGET_TEMPLATE_FOLDER = "templates/widget_project/"; private static final String WIDGET_MANIFEST_TEMPLATE_PATH = "templates/widget_project/AndroidWidgetManifest.template"; //$NON-NLS-1$ private static final String WIDGET_ACTIVITY_TEMPLATE_PATH = "templates/widget_project/activity.template"; //$NON-NLS-1$ private static final String WIDGET_RECEIVER_TEMPLATE_PATH = "templates/widget_project/receiver.template"; //$NON-NLS-1$ private static final String WIDGET_USES_SDK_TEMPLATE_PATH = "templates/widget_project/uses-sdk.template"; //$NON-NLS-1$ private static final String RECEIVERS = "RECEIVERS"; //$NON-NLS-1$ private static final String WIDGET_INITIAL_LAYOUT_XML = "widget_initial_layout.xml"; //$NON-NLS-1$ private static final String WIDGET_INFO_XML = "widget_info.xml"; //$NON-NLS-1$ private static final String WIDGET_PROVIDER_SAMPLE_NAME = "WidgetProvider"; //$NON-NLS-1$ private static final String WIDGET_PROVIDER_SAMPLE_TEMPLATE = "WidgetProvider.template"; //$NON-NLS-1$ private static final String IMPORT_RESOURCE_CLASS = "IMPORT_RESOURCE_CLASS"; /** * Create a new Android Project * @param androidProject * @param container * @return * @throws AndroidException */ public static boolean createProject(final AndroidProject androidProject, IWizardContainer container) throws AndroidException { boolean created = true; IWorkspace workspace = ResourcesPlugin.getWorkspace(); final IProject project = workspace.getRoot().getProject(androidProject.getName()); if (!canCreateProject(workspace.getRoot(), androidProject.getName())) { throw new AndroidException( AndroidNLS.EXC_ProjectCreationSupport_CannotCreateProjectReadOnlyWorkspace); } else { final IProjectDescription description = workspace.newProjectDescription(project.getName()); final Map<String, Object> parameters = new HashMap<String, Object>(); parameters.put(MIN_SDK_VERSION, androidProject.getMinSdkVersion()); if ((androidProject.getSourceType() == SourceTypes.NEW) || (androidProject.getSourceType() == SourceTypes.WIDGET)) { /* * An activity name can be of the form ".package.Class" or ".Class". * The initial dot is ignored, as it is always added later in the templates. */ String activityName = androidProject.getActivityName(); if (activityName.startsWith(".")) { //$NON-NLS-1$ activityName = activityName.substring(1); } parameters.put(ACTIVITY_NAME, androidProject.getActivityName()); parameters.put(PACKAGE_NAME, androidProject.getPackageName()); parameters.put(APPLICATION_NAME, STRING_RSRC_PREFIX + APP_NAME); parameters.put(IMPORT_RESOURCE_CLASS, ""); } /* * create a dictionary of string that will contain name+content. * we'll put all the strings into values/strings.xml */ final HashMap<String, String> stringDictionary = new HashMap<String, String>(); stringDictionary.put(APP_NAME, androidProject.getApplicationName()); if (!androidProject.isUsingDefaultLocation() && androidProject.isNewProject()) { Path destination = new Path(androidProject.getLocation()); description.setLocation(destination); if (!FileUtil.canWrite(destination.toFile())) { String errMsg = NLS.bind( AndroidNLS.EXC_ProjectCreationSupport_CannotCreateProjectReadOnlyDestination, destination.toOSString()); throw new AndroidException(errMsg); } if (!validateNewProjectLocationIsEmpty(destination)) { throw new AndroidException(AndroidNLS.UI_ProjectCreationSupport_NonEmptyFolder); } } if (androidProject.getSourceType() == SourceTypes.EXISTING) { Path destination = new Path(androidProject.getLocation()); description.setLocation(destination); } /* * Create a monitored operation to create the actual project */ WorkspaceModifyOperation op = new WorkspaceModifyOperation() { @Override protected void execute(IProgressMonitor monitor) throws InvocationTargetException { createProjectAsync(project, androidProject, description, monitor, parameters, stringDictionary); } }; /* * Run the operation in a different thread */ created = runAsyncOperation(op, container); } return created; } /** * Create android project. * @param project * @param androidProject * @param description * @param monitor * @param parameters * @param stringDictionary * @throws InvocationTargetException */ protected static void createProjectAsync(IProject project, AndroidProject androidProject, IProjectDescription description, IProgressMonitor monitor, Map<String, Object> parameters, Map<String, String> stringDictionary) throws InvocationTargetException { monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_CopyingSamplesMonitorTaskTitle, 1000); try { // Create project and open it project.create(description, new SubProgressMonitor(monitor, 100)); if (monitor.isCanceled()) { undoProjectCreation(project); throw new OperationCanceledException(); } project.open(IResource.BACKGROUND_REFRESH, new SubProgressMonitor(monitor, 100)); ProjectUtils.setupAndroidNatures(project, monitor); // Create folders in the project if they don't already exist createDefaultDir(project, IAndroidConstants.WS_ROOT, BIN_DIR, new SubProgressMonitor( monitor, 40)); createDefaultDir(project, IAndroidConstants.WS_ROOT, RES_DIR, new SubProgressMonitor( monitor, 40)); createDefaultDir(project, IAndroidConstants.WS_ROOT, ASSETS_DIR, new SubProgressMonitor(monitor, 40)); createDefaultDir(project, IAndroidConstants.WS_ROOT, GEN_DIR, new SubProgressMonitor( monitor, 40)); switch (androidProject.getSourceType()) { case NEW: // Create the source folders in the project if they don't already exist List<String> sourceFolders = androidProject.getSourceFolders(); for (String sourceFolder : sourceFolders) { createDefaultDir(project, IAndroidConstants.WS_ROOT, sourceFolder, new SubProgressMonitor(monitor, 40)); } // Create the resource folders in the project if they don't already exist. int apiLevel = androidProject.getSdkTarget().getVersion().getApiLevel(); if (apiLevel < 4) { createDefaultDir(project, RES_DIR, DRAWABLE_DIR + File.separator, new SubProgressMonitor(monitor, 40)); } else { for (String dpi : DPIS) { createDefaultDir(project, RES_DIR, DRAWABLE_DIR + "-" + dpi + File.separator, new SubProgressMonitor(monitor, 40)); } } createDefaultDir(project, RES_DIR, LAYOUT_DIR, new SubProgressMonitor(monitor, 40)); createDefaultDir(project, RES_DIR, VALUES_DIR, new SubProgressMonitor(monitor, 40)); // Create files in the project if they don't already exist createManifest(project, parameters, stringDictionary, new SubProgressMonitor( monitor, 80)); // add the default app icon addIcon(project, apiLevel, new SubProgressMonitor(monitor, 100)); // Create the default package components String primarySrcFolder = IAndroidConstants.FD_SOURCES; if (!sourceFolders.contains(IAndroidConstants.FD_SOURCES)) { primarySrcFolder = sourceFolders.get(0); } addInitialCode(project, primarySrcFolder, parameters, stringDictionary, new SubProgressMonitor(monitor, 200)); // add the string definition file if needed if (stringDictionary.size() > 0) { EclipseUtils.createOrUpdateDictionaryFile(project, stringDictionary, null, new SubProgressMonitor(monitor, 100)); } break; case EXISTING: createDefaultDir(project, IAndroidConstants.WS_ROOT, GEN_DIR, new SubProgressMonitor(monitor, 650)); break; case SAMPLE: monitor.setTaskName(AndroidNLS.UI_ProjectCreationSupport_CopyingSamplesMonitorMessage); FileUtil.copyDir(androidProject.getSample().getFolder(), project.getLocation() .toFile()); project.refreshLocal(IResource.DEPTH_INFINITE, new SubProgressMonitor(monitor, 650)); break; case WIDGET: // Create the source folders in the project if they don't already exist List<String> widgetSourceFolders = androidProject.getSourceFolders(); for (String sourceFolder : widgetSourceFolders) { createDefaultDir(project, IAndroidConstants.WS_ROOT, sourceFolder, new SubProgressMonitor(monitor, 40)); } // Create the resource folders in the project if they don't already exist. int widgetApiLevel = androidProject.getSdkTarget().getVersion().getApiLevel(); if (widgetApiLevel < 4) { createDefaultDir(project, RES_DIR, DRAWABLE_DIR + File.separator, new SubProgressMonitor(monitor, 40)); } else { for (String dpi : DPIS) { createDefaultDir(project, RES_DIR, DRAWABLE_DIR + "-" + dpi + File.separator, new SubProgressMonitor(monitor, 40)); } } createDefaultDir(project, RES_DIR, LAYOUT_DIR, new SubProgressMonitor(monitor, 40)); createDefaultDir(project, RES_DIR, VALUES_DIR, new SubProgressMonitor(monitor, 40)); createDefaultDir(project, RES_DIR, XML_DIR, new SubProgressMonitor(monitor, 40)); // Create files in the project if they don't already exist createWidgetManifest(project, parameters, stringDictionary, new SubProgressMonitor(monitor, 80)); // add the default app icon addIcon(project, widgetApiLevel, new SubProgressMonitor(monitor, 100)); // Create the default package components String widgetPrimarySrcFolder = IAndroidConstants.FD_SOURCES; if (!widgetSourceFolders.contains(IAndroidConstants.FD_SOURCES)) { primarySrcFolder = widgetSourceFolders.get(0); } addInitialWidgetCode(project, widgetPrimarySrcFolder, parameters, stringDictionary, new SubProgressMonitor(monitor, 200)); // add the string definition file if needed if (stringDictionary.size() > 0) { EclipseUtils.createOrUpdateDictionaryFile(project, stringDictionary, null, new SubProgressMonitor(monitor, 100)); } break; } // Setup class path IJavaProject javaProject = JavaCore.create(project); setupSourceFolders(javaProject, androidProject.getSourceFolders(), new SubProgressMonitor(monitor, 40)); // Set output location javaProject.setOutputLocation(project.getFolder(BIN_DIR).getFullPath(), new SubProgressMonitor(monitor, 40)); SdkUtils.associate(project, androidProject.getSdkTarget()); ProjectUtils.fixProject(project); } catch (CoreException e) { undoProjectCreation(project); throw new InvocationTargetException(e); } catch (IOException e) { undoProjectCreation(project); throw new InvocationTargetException(e); } finally { monitor.done(); } } /** * Add initial code * @param project * @param sourceFolder * @param parameters * @param stringDictionary * @param monitor * @throws CoreException * @throws IOException */ private static void addInitialCode(IProject project, String sourceFolder, Map<String, Object> parameters, Map<String, String> stringDictionary, IProgressMonitor monitor) throws CoreException, IOException { monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_Configuring_Sample_Source_Task, 700); try { IFolder pkgFolder = project.getFolder(sourceFolder); Map<String, Object> processed_parameters = processSampleActivity(parameters); String activityName = (String) processed_parameters.get(ACTIVITY_NAME); String packageName = (String) processed_parameters.get(PACKAGE_NAME); pkgFolder = createPackageFolders(new SubProgressMonitor(monitor, 300), pkgFolder, packageName); if (activityName != null) { createSampleActivity(new SubProgressMonitor(monitor, 200), pkgFolder, processed_parameters, activityName); } IFolder layoutfolder = project.getFolder(RES_DIR + LAYOUT_DIR); IFile file = layoutfolder.getFile(MAIN_LAYOUT_XML); if (!file.exists()) { copyTemplateFile(LAYOUT_TEMPLATE, file, parameters, new SubProgressMonitor(monitor, 100)); if (activityName != null) { stringDictionary .put(STRING_HELLO_WORLD, NLS.bind( AndroidNLS.GEN_ProjectCreationSupport_HelloWorldWithName, activityName)); } else { stringDictionary.put(STRING_HELLO_WORLD, AndroidNLS.GEN_ProjectCreationSupport_HelloWorldSimple); } monitor.worked(100); } } finally { monitor.done(); } } /** * Add initial widget code * @param project * @param sourceFolder * @param parameters * @param stringDictionary * @param monitor * @throws CoreException * @throws IOException */ private static void addInitialWidgetCode(IProject project, String sourceFolder, Map<String, Object> parameters, Map<String, String> stringDictionary, IProgressMonitor monitor) throws CoreException, IOException { monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_Configuring_Sample_Source_Task, 800); try { IFolder pkgFolder = project.getFolder(sourceFolder); Map<String, Object> processed_parameters = processSampleActivity(parameters); String activityName = (String) processed_parameters.get(ACTIVITY_NAME); String packageName = (String) processed_parameters.get(PACKAGE_NAME); pkgFolder = createPackageFolders(new SubProgressMonitor(monitor, 200), pkgFolder, packageName); // Create sample activity if (activityName != null) { createSampleActivity(new SubProgressMonitor(monitor, 100), pkgFolder, processed_parameters, activityName); } // Create sample widget provider createSampleWidgetProvider(new SubProgressMonitor(monitor, 100), pkgFolder, processed_parameters); // Layout xml file IFolder layoutfolder = project.getFolder(RES_DIR + LAYOUT_DIR); IFile file = layoutfolder.getFile(MAIN_LAYOUT_XML); if (!file.exists()) { copyTemplateFile(LAYOUT_TEMPLATE, file, parameters, new SubProgressMonitor(monitor, 100)); if (activityName != null) { stringDictionary .put(STRING_HELLO_WORLD, NLS.bind( AndroidNLS.GEN_ProjectCreationSupport_HelloWorldWithName, activityName)); } else { stringDictionary.put(STRING_HELLO_WORLD, AndroidNLS.GEN_ProjectCreationSupport_HelloWorldSimple); } monitor.worked(100); } // Widget initial layout xml file IFile initial_layout_file = layoutfolder.getFile(WIDGET_INITIAL_LAYOUT_XML); if (!initial_layout_file.exists()) { copyWidgetTemplateFile(WIDGET_INITIAL_LAYOUT_XML, initial_layout_file, processed_parameters, new SubProgressMonitor(monitor, 100)); monitor.worked(100); } // Widget info xml file IFolder xmlFolder = project.getFolder(RES_DIR + XML_DIR); IFile widget_info_file = xmlFolder.getFile(WIDGET_INFO_XML); if (!widget_info_file.exists()) { copyWidgetTemplateFile(WIDGET_INFO_XML, widget_info_file, processed_parameters, new SubProgressMonitor(monitor, 100)); monitor.worked(100); } } finally { monitor.done(); } } private static void createSampleActivity(IProgressMonitor monitor, IFolder pkgFolder, Map<String, Object> processed_parameters, String activityName) throws CoreException, IOException { monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_Configuring_Sample_Activity_Task, 100); try { IFile file = pkgFolder.getFile(activityName + IAndroidConstants.DOT_JAVA); if (!file.exists()) { monitor.worked(10); copyTemplateFile(JAVA_ACTIVITY_TEMPLATE, file, processed_parameters, new SubProgressMonitor(monitor, 90)); } } finally { monitor.done(); } } private static void createSampleWidgetProvider(IProgressMonitor monitor, IFolder pkgFolder, Map<String, Object> processed_parameters) throws CoreException, IOException { monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_Configuring_Sample_Widget_Provider, 100); try { IFile file = pkgFolder.getFile(WIDGET_PROVIDER_SAMPLE_NAME + IAndroidConstants.DOT_JAVA); if (!file.exists()) { monitor.worked(10); copyWidgetTemplateFile(WIDGET_PROVIDER_SAMPLE_TEMPLATE, file, processed_parameters, new SubProgressMonitor(monitor, 90)); } } finally { monitor.done(); } } private static IFolder createPackageFolders(IProgressMonitor monitor, IFolder pkgFolder, String packageName) throws CoreException { String[] components = packageName.split(IAndroidConstants.RE_DOT); monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_Preparing_Java_Packages_Task, components.length * 100); try { for (String component : components) { pkgFolder = pkgFolder.getFolder(component); if (!pkgFolder.exists()) { pkgFolder.create(true, true, new SubProgressMonitor(monitor, 100)); } } } finally { monitor.done(); } return pkgFolder; } private static Map<String, Object> processSampleActivity(Map<String, Object> parameters) { String activityName = (String) parameters.get(ACTIVITY_NAME); Map<String, Object> processed_parameters = new HashMap<String, Object>(parameters); if ((activityName != null) && activityName.contains(".")) //$NON-NLS-1$ { String packageName = (String) parameters.get(PACKAGE_NAME); packageName += "." + activityName.substring(0, activityName.lastIndexOf('.')); //$NON-NLS-1$ activityName = activityName.substring(activityName.lastIndexOf('.')); processed_parameters.put(PACKAGE_NAME, packageName); processed_parameters.put(ACTIVITY_NAME, activityName); } return processed_parameters; } /** * Copy template files * @param resourceFilename * @param destFile * @param parameters * @param monitor * @throws CoreException * @throws IOException */ private static void copyTemplateFile(String resourceFilename, IFile destFile, Map<String, Object> parameters, IProgressMonitor monitor) throws CoreException, IOException { monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_Preparing_Template_File_Task, 150); InputStream stream = null; try { String template = readEmbeddedTextFileADT(TEMPLATES_DIRECTORY + resourceFilename, parameters); monitor.worked(50); stream = new ByteArrayInputStream(template.getBytes("UTF-8")); //$NON-NLS-1$ destFile.create(stream, false, new SubProgressMonitor(monitor, 100)); } finally { if (stream != null) { stream.close(); } monitor.done(); } } /** * Copy widget template files * @param resourceFilename * @param destFile * @param parameters * @param monitor * @throws CoreException * @throws IOException */ private static void copyWidgetTemplateFile(String resourceFilename, IFile destFile, Map<String, Object> parameters, IProgressMonitor monitor) throws CoreException, IOException { monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_Preparing_Template_File_Task, 150); InputStream stream = null; try { String template = readEmbeddedTextFileStudio(WIDGET_TEMPLATE_FOLDER + resourceFilename, parameters); monitor.worked(50); stream = new ByteArrayInputStream(template.getBytes("UTF-8")); //$NON-NLS-1$ destFile.create(stream, false, new SubProgressMonitor(monitor, 100)); } finally { if (stream != null) { stream.close(); } monitor.done(); } } /** * Add Icon to the project * @param project * @param apiLevel * @param monitor * @throws CoreException */ private static void addIcon(IProject project, int apiLevel, IProgressMonitor monitor) throws CoreException { monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_Configuring_Project_Icon_Task, 1000); try { if (apiLevel < 4) { IFile imageFile = project.getFile(RES_DIR + IPath.SEPARATOR + DRAWABLE_DIR + IPath.SEPARATOR + ICON); if (!imageFile.exists()) { String fileName = ICON.substring(0, ICON.length() - 4) + "_" + DPIS[2] + ICON.substring(ICON.length() - 4); createImageFromTemplate(monitor, imageFile, fileName); } } else { for (String dpi : DPIS) { IFile imageFile = project.getFile(RES_DIR + IPath.SEPARATOR + DRAWABLE_DIR + "-" + dpi + IPath.SEPARATOR + ICON); if (!imageFile.exists()) { String fileName = ICON.substring(0, ICON.length() - 4) + "_" + dpi + ICON.substring(ICON.length() - 4); createImageFromTemplate(monitor, imageFile, fileName); } } } } finally { monitor.done(); } } private static void createImageFromTemplate(IProgressMonitor monitor, IFile imageFile, String fileName) throws CoreException { byte[] buffer = AdtPlugin.readEmbeddedFile(TEMPLATES_DIRECTORY + fileName); if (buffer != null) { InputStream stream = null; try { stream = new ByteArrayInputStream(buffer); imageFile.create(stream, IResource.NONE, new SubProgressMonitor(monitor, 1000)); } finally { try { stream.close(); } catch (IOException e) { StudioLogger.info("Create image from template could not close stream. " + e.getMessage()); } } } } /** * Adds the manifest to the project. * @param project * @param parameters * @param stringDictionary * @param monitor * @throws CoreException * @throws IOException */ private static void createManifest(IProject project, Map<String, Object> parameters, Map<String, String> stringDictionary, IProgressMonitor monitor) throws CoreException, IOException { monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_Creating_Manifest_File_Task, 300); try { IFile manifestFile = project.getFile(IAndroidConstants.FN_ANDROID_MANIFEST); if (!manifestFile.exists()) { monitor.setTaskName(AndroidNLS.UI_ProjectCreationSupport_Reading_Template_File_Task); String manifestTemplate = readEmbeddedTextFileADT(MANIFEST_TEMPLATE, parameters); monitor.worked(10); if (parameters.containsKey(ACTIVITY_NAME)) { String activities = readEmbeddedTextFileADT(ACTIVITY_TEMPLATE, parameters); String intent = AdtPlugin.readEmbeddedTextFile(LAUNCHER_INTENT_TEMPLATE); activities = activities.replaceAll(INTENT_FILTERS, intent); manifestTemplate = manifestTemplate.replaceAll(ACTIVITIES, activities); monitor.worked(90); } else { manifestTemplate = manifestTemplate.replaceAll(ACTIVITIES, ""); //$NON-NLS-1$ monitor.worked(90); } //We don't currently supports the TEST parameters. So let's just remove the unused tags. manifestTemplate = manifestTemplate.replaceAll(TEST_USES_LIBRARY, ""); //$NON-NLS-1$ manifestTemplate = manifestTemplate.replaceAll(TEST_INSTRUMENTATION, ""); //$NON-NLS-1$ String minSdkVersion = (String) parameters.get(MIN_SDK_VERSION); if ((minSdkVersion != null) && (minSdkVersion.length() > 0)) { String usesSdk = readEmbeddedTextFileADT(USES_SDK_TEMPLATE, parameters); manifestTemplate = manifestTemplate.replaceAll(USES_SDK, usesSdk); monitor.worked(50); } else { manifestTemplate = manifestTemplate.replaceAll(USES_SDK, ""); //$NON-NLS-1$ monitor.worked(50); } InputStream stream = null; try { stream = new ByteArrayInputStream(manifestTemplate.getBytes("UTF-8")); //$NON-NLS-1$ manifestFile.create(stream, IResource.NONE, new SubProgressMonitor(monitor, 150)); } finally { try { if (stream != null) { stream.close(); } } catch (IOException e) { StudioLogger.info("Could not close stream while creating manifest", e.getMessage()); } } } } finally { monitor.done(); } } /** * Adds the widget manifest to the project. * @param project * @param parameters * @param stringDictionary * @param monitor * @throws CoreException * @throws IOException */ private static void createWidgetManifest(IProject project, Map<String, Object> parameters, Map<String, String> stringDictionary, IProgressMonitor monitor) throws CoreException, IOException { monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_Creating_Manifest_File_Task, 300); try { IFile manifestFile = project.getFile(IAndroidConstants.FN_ANDROID_MANIFEST); if (!manifestFile.exists()) { monitor.setTaskName(AndroidNLS.UI_ProjectCreationSupport_Reading_Template_File_Task); // Manifest skeleton String manifestTemplate = readEmbeddedTextFileStudio(WIDGET_MANIFEST_TEMPLATE_PATH, parameters); monitor.worked(10); // Activity information if (parameters.containsKey(ACTIVITY_NAME)) { String activities = readEmbeddedTextFileStudio(WIDGET_ACTIVITY_TEMPLATE_PATH, parameters); manifestTemplate = manifestTemplate.replaceAll(ACTIVITIES, activities); monitor.worked(70); } else { manifestTemplate = manifestTemplate.replaceAll(ACTIVITIES, ""); //$NON-NLS-1$ monitor.worked(70); } // Receiver information String receivers = readEmbeddedTextFileStudio(WIDGET_RECEIVER_TEMPLATE_PATH, parameters); manifestTemplate = manifestTemplate.replaceAll(RECEIVERS, receivers); monitor.worked(70); // Min Sdk information String minSdkVersion = (String) parameters.get(MIN_SDK_VERSION); if ((minSdkVersion != null) && (minSdkVersion.length() > 0)) { String usesSdk = readEmbeddedTextFileStudio(WIDGET_USES_SDK_TEMPLATE_PATH, parameters); manifestTemplate = manifestTemplate.replaceAll(USES_SDK, usesSdk); monitor.worked(50); } else { manifestTemplate = manifestTemplate.replaceAll(USES_SDK, ""); //$NON-NLS-1$ monitor.worked(50); } InputStream stream = null; try { stream = new ByteArrayInputStream(manifestTemplate.getBytes("UTF-8")); //$NON-NLS-1$ manifestFile.create(stream, IResource.NONE, new SubProgressMonitor(monitor, 100)); } finally { try { if (stream != null) { stream.close(); } } catch (IOException e) { StudioLogger.info( "Could not close stream while creating manifest for widget", e.getMessage()); } } } } finally { monitor.done(); } } private static String readEmbeddedTextFileADT(String template, Map<String, Object> parameters) { String loadedTemplate = AdtPlugin.readEmbeddedTextFile(template); for (String key : parameters.keySet()) { if (parameters.get(key) instanceof String) { loadedTemplate = loadedTemplate.replaceAll(key, (String) parameters.get(key)); } } return loadedTemplate; } private static String readEmbeddedTextFileStudio(String template, Map<String, Object> parameters) { String loadedTemplate = EclipseUtils.readEmbeddedResource(AndroidPlugin.getDefault().getBundle(), template); for (String key : parameters.keySet()) { if (parameters.get(key) instanceof String) { loadedTemplate = loadedTemplate.replaceAll(key, (String) parameters.get(key)); } } return loadedTemplate; } /** * Setup src folders * @param javaProject * @param sourceFolder * @param monitor * @throws JavaModelException */ private static void setupSourceFolders(IJavaProject javaProject, List<String> sourceFolders, IProgressMonitor monitor) throws JavaModelException { monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_Preparing_Source_Folders_Task, (sourceFolders.size() * 100) + 100); try { IProject project = javaProject.getProject(); IClasspathEntry[] entries = javaProject.getRawClasspath(); for (String sourceFolder : sourceFolders) { IFolder srcFolder = project.getFolder(sourceFolder); entries = removeClasspathEntry(entries, srcFolder); entries = removeClasspathEntry(entries, srcFolder.getParent()); entries = ProjectUtils.addEntryToClasspath(entries, JavaCore.newSourceEntry(srcFolder.getFullPath())); monitor.worked(100); } javaProject.setRawClasspath(entries, new SubProgressMonitor(monitor, 100)); } finally { monitor.done(); } } /** * Remove source folder from classpath * @param entries * @param folder * @return */ private static IClasspathEntry[] removeClasspathEntry(IClasspathEntry[] entries, IContainer folder) { IClasspathEntry[] newClassPath = null; if (folder != null) { IClasspathEntry removeEntry = JavaCore.newSourceEntry(folder.getFullPath()); List<IClasspathEntry> entriesList = Arrays.asList(entries); if (entriesList.contains(removeEntry)) { newClassPath = new IClasspathEntry[entries.length - 1]; int i = 0; for (IClasspathEntry entry : entriesList) { if (!entry.equals(removeEntry)) { newClassPath[i] = entry; i++; } } } else { newClassPath = entries; } } else { newClassPath = entries; } return newClassPath; } /** * Add default directory to Project * @param project * @param parentFolder * @param folderName * @param monitor * @throws CoreException */ private static void createDefaultDir(IProject project, String parentFolder, String folderName, IProgressMonitor monitor) throws CoreException { monitor.beginTask( AndroidNLS.UI_ProjectCreationSupport_Creating_Directory_Task + folderName, 100); try { monitor.setTaskName(AndroidNLS.UI_ProjectCreationSupport_Verifying_Directory_Task); if (folderName.length() > 0) { monitor.worked(10); IFolder folder = project.getFolder(parentFolder + folderName); monitor.worked(10); if (!folder.exists()) { monitor.worked(10); if (FileUtil.canWrite(folder.getLocation().toFile())) { monitor.worked(10); monitor.setTaskName(AndroidNLS.UI_ProjectCreationSupport_Creating_Directory_Task); folder.create(true, true, new SubProgressMonitor(monitor, 60)); } else { String errMsg = NLS.bind( AndroidNLS.EXC_ProjectCreationSupport_CannotCreateFolderReadOnlyWorkspace, folder.getLocation().toFile().toString()); IStatus status = new AndroidStatus(IStatus.ERROR, errMsg); throw new CoreException(status); } } } } finally { monitor.done(); } } /** * Validate new Project Location. * @param destination * @param display * @return */ public static boolean validateNewProjectLocationIsEmpty(IPath destination) { File f = new File(destination.toOSString()); if (f.isDirectory() && (f.list().length > 0)) { // EclipseUtils.showErrorDialog( // AndroidNLS.UI_ProjectCreationSupport_NonEmptyFolderQuestionDialogTitle, // AndroidNLS.UI_ProjectCreationSupport_NonEmptyFolderQuestion); return false; } return true; } /** * Run the operation in async thread. * @param op * @param container * * @return true if no errors occur during the operation or false otherwise */ private static boolean runAsyncOperation(WorkspaceModifyOperation op, IWizardContainer container) { boolean created = false; try { container.run(true, true, op); created = true; } catch (InvocationTargetException ite) { Throwable t = ite.getTargetException(); if (t instanceof CoreException) { CoreException core = (CoreException) t; if (core.getStatus().getCode() == IResourceStatus.CASE_VARIANT_EXISTS) { MessageDialog.openError(container.getShell(), AndroidNLS.UI_GenericErrorDialogTitle, AndroidNLS.ERR_ProjectCreationSupport_CaseVariantExistsError); } else { ErrorDialog.openError(container.getShell(), AndroidNLS.UI_GenericErrorDialogTitle, null, core.getStatus()); } } else { MessageDialog.openError(container.getShell(), AndroidNLS.UI_GenericErrorDialogTitle, t.getMessage()); } } catch (InterruptedException e) { StudioLogger.error(ProjectCreationSupport.class, "Error creating project.", e); //$NON-NLS-1$ } return created; } /** * Checks if a project can be created on workspace * * @param root The workspace root * @param projectName The project name * * @return true if the project can be created or false otherwise */ private static boolean canCreateProject(IWorkspaceRoot root, String projectName) { File rootFolder = root.getLocation().toFile(); File projectFolder = new File(rootFolder, projectName); return FileUtil.canWrite(projectFolder); } /** * Undoes a project creation. Removes all files created by the project creation process * and keeps any other previous files * * @param project The failed project * @param existingResources A set containing the path of pre-existing resources before creating * the project */ private static void undoProjectCreation(IProject project) { File projectPath = new File(project.getWorkspace().getRoot().getLocation().toFile(), project.getName()); Set<String> existingResources = getExistingResources(projectPath); try { project.delete(false, true, new NullProgressMonitor()); } catch (CoreException e1) { // Do nothing StudioLogger.error(ProjectCreationSupport.class, e1.getLocalizedMessage(), e1); } if (existingResources.isEmpty()) { try { FileUtil.deleteDirRecursively(project.getLocation().toFile()); } catch (IOException e) { // Do nothing StudioLogger.error(ProjectCreationSupport.class, e.getLocalizedMessage(), e); } } else { File root = new File(project.getWorkspace().getRoot().getLocation().toFile(), project.getName()); removeCreatedResources(root, existingResources); } } /** * Retrieves a list of existing sub-resources from a folder * * @param folder the File object representing the folder * * @return a list of existing sub-resources from the folder */ private static Set<String> getExistingResources(File folder) { Set<String> existing = new HashSet<String>(); if ((folder != null) && folder.exists() && folder.isDirectory()) { existing.add(folder.toString()); File[] children = folder.listFiles(); if (children != null) { for (File child : children) { if (child.isDirectory()) { existing.addAll(getExistingResources(child)); } else { existing.add(child.toString()); } } } } return existing; } /** * Removes the created resources by a failed project creation process * * @param startingPoint The project root folder (File object) * @param existingResources The set containing the previous existing resources in the project root folder */ private static void removeCreatedResources(File startingPoint, Set<String> existingResources) { File[] members = startingPoint.listFiles(); if (members != null) { for (File child : members) { if (child.isFile()) { if (!existingResources.contains(child.toString())) { child.delete(); } } else { removeCreatedResources(child, existingResources); } } } if (!existingResources.contains(startingPoint.toString())) { startingPoint.delete(); } } }