blob: 06c0300b7f286179d80fc2f3d95e1a99139ef3a3 [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
*
* 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.android.ide.eclipse.adt.internal.wizards.newproject;
import com.android.SdkConstants;
import com.android.annotations.Nullable;
import com.android.ide.common.xml.ManifestData;
import com.android.ide.common.xml.ManifestData.Activity;
import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.internal.project.ProjectProperties;
import com.android.sdklib.internal.project.ProjectProperties.PropertyType;
import com.android.utils.Pair;
import com.android.xml.AndroidManifest;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.ui.IWorkingSet;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**
* The {@link NewProjectWizardState} holds the state used by the various pages
* in the {@link NewProjectWizard} and its variations, and it can also be used
* to pass project information to the {@link NewProjectCreator}.
*/
public class NewProjectWizardState {
/** The mode to run the wizard in: creating test, or sample, or plain project */
public Mode mode;
/**
* If true, the project should be created from an existing codebase (pointed
* to by the {@link #projectLocation} or in the case of sample projects, the
* {@link #chosenSample}. Otherwise, create a brand new project from scratch.
*/
public boolean useExisting;
/**
* Whether new projects should be created into the default project location
* (e.g. in the Eclipse workspace) or not
*/
public boolean useDefaultLocation = true;
/** The build target SDK */
public IAndroidTarget target;
/** True if the user has manually modified the target */
public boolean targetModifiedByUser;
/** The location to store projects into */
public File projectLocation = new File(Platform.getLocation().toOSString());
/** True if the project location name has been manually edited by the user */
public boolean projectLocationModifiedByUser;
/** The name of the project */
public String projectName = ""; //$NON-NLS-1$
/** True if the project name has been manually edited by the user */
public boolean projectNameModifiedByUser;
/** The application name */
public String applicationName;
/** True if the application name has been manually edited by the user */
public boolean applicationNameModifiedByUser;
/** The package path */
public String packageName;
/** True if the package name has been manually edited by the user */
public boolean packageNameModifiedByUser;
/** True if a new activity should be created */
public boolean createActivity;
/** The name of the new activity to be created */
public String activityName;
/** True if the activity name has been manually edited by the user */
public boolean activityNameModifiedByUser;
/** The minimum SDK version to use with the project (may be null or blank) */
public String minSdk;
/** True if the minimum SDK version has been manually edited by the user */
public boolean minSdkModifiedByUser;
/**
* A list of paths to each of the available samples for the current SDK.
* The pair is (String: sample display name => File: sample directory).
* Note we want a list, not a map since we might have duplicates.
* */
public List<Pair<String, File>> samples = new ArrayList<Pair<String, File>>();
/** Path to the currently chosen sample */
public File chosenSample;
/** The name of the source folder, relative to the project root */
public String sourceFolder = SdkConstants.FD_SOURCES;
/** The set of chosen working sets to use when creating the project */
public IWorkingSet[] workingSets = new IWorkingSet[0];
/**
* A reference to a different project that the current test project will be
* testing.
*/
public IProject testedProject;
/**
* If true, this test project should be testing itself, otherwise it will be
* testing the project pointed to by {@link #testedProject}.
*/
public boolean testingSelf;
// NOTE: These apply only to creating paired projects; when isTest is true
// we're using
// the normal fields above
/**
* If true, create a test project along with this plain project which will
* be testing the plain project. (This flag only applies when creating
* normal projects.)
*/
public boolean createPairProject;
/**
* The application name of the test application (only applies when
* {@link #createPairProject} is true)
*/
public String testApplicationName;
/**
* True if the testing application name has been modified by the user (only
* applies when {@link #createPairProject} is true)
*/
public boolean testApplicationNameModified;
/**
* The package name of the test application (only applies when
* {@link #createPairProject} is true)
*/
public String testPackageName;
/**
* True if the testing package name has been modified by the user (only
* applies when {@link #createPairProject} is true)
*/
public boolean testPackageModified;
/**
* The project name of the test project (only applies when
* {@link #createPairProject} is true)
*/
public String testProjectName;
/**
* True if the testing project name has been modified by the user (only
* applies when {@link #createPairProject} is true)
*/
public boolean testProjectModified;
/** Package name of the tested app */
public String testTargetPackageName;
/**
* Copy project into workspace? This flag only applies when importing
* projects (creating projects from existing source)
*/
public boolean copyIntoWorkspace;
/**
* List of projects to be imported. Null if not importing projects.
*/
@Nullable
public List<ImportedProject> importProjects;
/**
* Creates a new {@link NewProjectWizardState}
*
* @param mode the mode to run the wizard in
*/
public NewProjectWizardState(Mode mode) {
this.mode = mode;
if (mode == Mode.SAMPLE) {
useExisting = true;
} else if (mode == Mode.TEST) {
createActivity = false;
}
}
/**
* Extract information (package name, application name, minimum SDK etc) from
* the given Android project.
*
* @param path the path to the project to extract information from
*/
public void extractFromAndroidManifest(Path path) {
String osPath = path.append(SdkConstants.FN_ANDROID_MANIFEST_XML).toOSString();
if (!(new File(osPath).exists())) {
return;
}
ManifestData manifestData = AndroidManifestHelper.parseForData(osPath);
if (manifestData == null) {
return;
}
String newPackageName = null;
Activity activity = null;
String newActivityName = null;
String minSdkVersion = null;
try {
newPackageName = manifestData.getPackage();
minSdkVersion = manifestData.getMinSdkVersionString();
// try to get the first launcher activity. If none, just take the first activity.
activity = manifestData.getLauncherActivity();
if (activity == null) {
Activity[] activities = manifestData.getActivities();
if (activities != null && activities.length > 0) {
activity = activities[0];
}
}
} catch (Exception e) {
// ignore exceptions
}
if (newPackageName != null && newPackageName.length() > 0) {
packageName = newPackageName;
}
if (activity != null) {
newActivityName = AndroidManifest.extractActivityName(activity.getName(),
newPackageName);
}
if (newActivityName != null && newActivityName.length() > 0) {
activityName = newActivityName;
// we are "importing" an existing activity, not creating a new one
createActivity = false;
// If project name and application names are empty, use the activity
// name as a default. If the activity name has dots, it's a part of a
// package specification and only the last identifier must be used.
if (newActivityName.indexOf('.') != -1) {
String[] ids = newActivityName.split(AdtConstants.RE_DOT);
newActivityName = ids[ids.length - 1];
}
if (projectName == null || projectName.length() == 0 ||
!projectNameModifiedByUser) {
projectName = newActivityName;
projectNameModifiedByUser = false;
}
if (applicationName == null || applicationName.length() == 0 ||
!applicationNameModifiedByUser) {
applicationNameModifiedByUser = false;
applicationName = newActivityName;
}
} else {
activityName = ""; //$NON-NLS-1$
// There is no activity name to use to fill in the project and application
// name. However if there's a package name, we can use this as a base.
if (newPackageName != null && newPackageName.length() > 0) {
// Package name is a java identifier, so it's most suitable for
// an application name.
if (applicationName == null || applicationName.length() == 0 ||
!applicationNameModifiedByUser) {
applicationName = newPackageName;
}
// For the project name, remove any dots
newPackageName = newPackageName.replace('.', '_');
if (projectName == null || projectName.length() == 0 ||
!projectNameModifiedByUser) {
projectName = newPackageName;
}
}
}
if (mode == Mode.ANY && useExisting) {
updateSdkTargetToMatchProject(path.toFile());
}
minSdk = minSdkVersion;
minSdkModifiedByUser = false;
}
/**
* Try to find an SDK Target that matches the current MinSdkVersion.
*
* There can be multiple targets with the same sdk api version, so don't change
* it if it's already at the right version. Otherwise pick the first target
* that matches.
*/
public void updateSdkTargetToMatchMinSdkVersion() {
IAndroidTarget currentTarget = target;
if (currentTarget != null && currentTarget.getVersion().equals(minSdk)) {
return;
}
Sdk sdk = Sdk.getCurrent();
if (sdk != null) {
IAndroidTarget[] targets = sdk.getTargets();
for (IAndroidTarget t : targets) {
if (t.getVersion().equals(minSdk)) {
target = t;
return;
}
}
}
}
/**
* Updates the SDK to reflect the SDK required by the project at the given
* location
*
* @param location the location of the project
*/
public void updateSdkTargetToMatchProject(File location) {
// Select the target matching the manifest's sdk or build properties, if any
IAndroidTarget foundTarget = null;
// This is the target currently in the UI
IAndroidTarget currentTarget = target;
String projectPath = location.getPath();
// If there's a current target defined, we do not allow to change it when
// operating in the create-from-sample mode -- since the available sample list
// is tied to the current target, so changing it would invalidate the project we're
// trying to load in the first place.
if (!targetModifiedByUser) {
ProjectProperties p = ProjectProperties.load(projectPath,
PropertyType.PROJECT);
if (p != null) {
String v = p.getProperty(ProjectProperties.PROPERTY_TARGET);
IAndroidTarget desiredTarget = Sdk.getCurrent().getTargetFromHashString(v);
// We can change the current target if:
// - we found a new desired target
// - there is no current target
// - or the current target can't run the desired target
if (desiredTarget != null &&
(currentTarget == null || !desiredTarget.canRunOn(currentTarget))) {
foundTarget = desiredTarget;
}
}
Sdk sdk = Sdk.getCurrent();
IAndroidTarget[] targets = null;
if (sdk != null) {
targets = sdk.getTargets();
}
if (targets == null) {
targets = new IAndroidTarget[0];
}
if (foundTarget == null && minSdk != null) {
// Otherwise try to match the requested min-sdk-version if we find an
// exact match, regardless of the currently selected target.
for (IAndroidTarget existingTarget : targets) {
if (existingTarget != null &&
existingTarget.getVersion().equals(minSdk)) {
foundTarget = existingTarget;
break;
}
}
}
if (foundTarget == null) {
// Or last attempt, try to match a sample project location and use it
// if we find an exact match, regardless of the currently selected target.
for (IAndroidTarget existingTarget : targets) {
if (existingTarget != null &&
projectPath.startsWith(existingTarget.getLocation())) {
foundTarget = existingTarget;
break;
}
}
}
}
if (foundTarget != null) {
target = foundTarget;
}
}
/**
* Type of project being offered/created by the wizard
*/
public enum Mode {
/** Create a sample project. Testing options are not presented. */
SAMPLE,
/**
* Create a test project, either testing itself or some other project.
* Note that even if in the {@link #ANY} mode, a test project can be
* created as a *paired* project with the main project, so this flag
* only means that we are creating *just* a test project
*/
TEST,
/**
* Create an Android project, which can be a plain project, optionally
* with a paired test project, or a sample project (the first page
* contains toggles for choosing which
*/
ANY;
}
}