blob: 83a92f7b07d2a1531cd19e164e0ba1f92aaf5abd [file] [log] [blame]
/*
* Copyright (C) 2014 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.android.tools.idea.npw;
import com.android.tools.idea.ui.LabelWithEditLink;
import com.android.tools.idea.wizard.WizardConstants;
import com.android.tools.idea.wizard.dynamic.DynamicWizardStepWithHeaderAndDescription;
import com.android.tools.idea.wizard.dynamic.ScopedStateStore;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.android.util.AndroidUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.text.Document;
import java.util.Set;
import static com.android.tools.idea.npw.ConfigureAndroidProjectStep.PACKAGE_NAME_DERIVER;
import static com.android.tools.idea.npw.ConfigureAndroidProjectStep.SAVED_COMPANY_DOMAIN;
import static com.android.tools.idea.wizard.WizardConstants.*;
/**
* Configuration for a new Android module
*/
public class ConfigureAndroidModuleStepDynamic extends DynamicWizardStepWithHeaderAndDescription {
private static final Logger LOG = Logger.getInstance(ConfigureAndroidModuleStepDynamic.class);
private CreateModuleTemplate myModuleType;
private FormFactorApiComboBox mySdkControls;
private JTextField myModuleName;
private JPanel myPanel;
private JTextField myAppName;
private LabelWithEditLink myPackageName;
public ConfigureAndroidModuleStepDynamic(@Nullable Disposable parentDisposable) {
super("Configure your new module", null, parentDisposable);
setBodyComponent(myPanel);
}
@Override
public void init() {
String projectLocation = myState.get(PROJECT_LOCATION_KEY);
super.init();
myState.put(PROJECT_LOCATION_KEY, projectLocation);
CreateModuleTemplate template = getFormfactorModuleTemplate();
assert template != null;
register(FormFactorUtils.getModuleNameKey(template.getFormFactor()), myModuleName);
mySdkControls.init(template.getFormFactor(), template.getMetadata().getMinSdk());
mySdkControls.register(this);
register(WizardConstants.APPLICATION_NAME_KEY, myAppName);
register(WizardConstants.PACKAGE_NAME_KEY, myPackageName);
registerValueDeriver(WizardConstants.PACKAGE_NAME_KEY, PACKAGE_NAME_DERIVER);
if (StringUtil.isEmptyOrSpaces(myState.get(APPLICATION_NAME_KEY))) {
String name = myState.getNotNull(WizardConstants.IS_LIBRARY_KEY, false) ? "My Library" : "My Application";
myState.put(WizardConstants.APPLICATION_NAME_KEY, name);
String savedCompanyDomain = PropertiesComponent.getInstance().getValue(SAVED_COMPANY_DOMAIN);
myState.put(WizardConstants.COMPANY_DOMAIN_KEY, savedCompanyDomain);
}
super.init();
}
private void createUIComponents() {
mySdkControls = new FormFactorApiComboBox();
}
@Override
public void onEnterStep() {
super.onEnterStep();
CreateModuleTemplate template = getFormfactorModuleTemplate();
if (template != null && template.getFormFactor() != null && template.getMetadata() != null) {
myModuleType = template;
registerValueDeriver(FormFactorUtils.getModuleNameKey(template.getFormFactor()), ourModuleNameDeriver);
} else {
LOG.error("init() Called on ConfigureAndroidModuleStepDynamic with an incorrect selected ModuleType");
}
if (mySdkControls != null) {
mySdkControls.loadSavedApi();
}
invokeUpdate(null);
}
@Override
public boolean commitStep() {
boolean commit = super.commitStep();
if (commit && myPath instanceof NewFormFactorModulePath) {
// Fix for https://code.google.com/p/android/issues/detail?id=78730
// where creating a new module results in the activity classes with the
// wrong package name.
//
// The reason that happens is that for some reason the packageName
// parameter in the TemplateParameterStep2 state does not get updated.
// And that happens because it's *normally* updated by
// NewFormFactorModulePath#onPathStarted, which in the New Project Wizard
// is called *after* the user has edited the package name in the first
// wizard panel and has moved on to the next panel. That means that when
// onPathStarted is called, and it pushes the default package into the
// TemplateParameterStep2 map, it contains the updated package.
//
// However, in the new module wizard, NewFormFactorModulePath#onPathStarted
// is called *before* the package has been edited.
//
// Having those values be "committed" from onPathStarted in NewFormFactorModule
// seems wrong, but since we're in high resistance, we're going for a minimal
// impact fix here: Fix this specifically for the New Module Wizard (this class)
// where we deliberately update those values only when the values are
// actually committed from the new module wizard package panel.
((NewFormFactorModulePath)myPath).updatePackageDerivedValues();
}
return commit;
}
@Nullable
private CreateModuleTemplate getFormfactorModuleTemplate() {
ModuleTemplate moduleTemplate = myState.get(SELECTED_MODULE_TYPE_KEY);
if (moduleTemplate instanceof CreateModuleTemplate) {
CreateModuleTemplate type = (CreateModuleTemplate)moduleTemplate;
if (type.getFormFactor() != null && type.getMetadata() != null) {
return type;
}
}
return null;
}
@Override
public void deriveValues(Set<ScopedStateStore.Key> modified) {
super.deriveValues(modified);
if (mySdkControls != null) {
mySdkControls.deriveValues(myState, modified);
}
}
@Override
public boolean validate() {
setErrorHtml("");
return validateAppName() && validatePackageName() && validateApiLevel();
}
private boolean validateApiLevel() {
if (mySdkControls == null || mySdkControls.getItemCount() < 1) {
setErrorHtml("No supported platforms found. Please install the proper platform or add-on through the SDK manager.");
return false;
}
return true;
}
private final ValueDeriver<String> ourModuleNameDeriver = new ValueDeriver<String>() {
@Nullable
@Override
public Set<ScopedStateStore.Key<?>> getTriggerKeys() {
return makeSetOf(APPLICATION_NAME_KEY);
}
@Nullable
@Override
public String deriveValue(@NotNull ScopedStateStore state, @Nullable ScopedStateStore.Key changedKey, @Nullable String currentValue) {
String appName = state.get(APPLICATION_NAME_KEY);
if (appName == null) {
appName = myModuleType.getFormFactor().toString();
}
return WizardUtils.computeModuleName(appName, getProject());
}
};
protected boolean validateAppName() {
String appName = myState.get(APPLICATION_NAME_KEY);
if (appName == null || appName.isEmpty()) {
setErrorHtml("Please enter an application name (shown in launcher), or a descriptive name for your library");
return false;
} else if (Character.isLowerCase(appName.charAt(0))) {
setErrorHtml("The application name for most apps begins with an uppercase letter");
}
return true;
}
protected boolean validatePackageName() {
String packageName = myState.get(WizardConstants.PACKAGE_NAME_KEY);
if (packageName == null) {
setErrorHtml("Please enter a package name (This package uniquely identifies your application or library)");
return false;
} else {
String message = AndroidUtils.validateAndroidPackageName(packageName);
if (message != null) {
setErrorHtml("Invalid package name: " + message);
return false;
}
}
return true;
}
@Override
public boolean isStepVisible() {
return getFormfactorModuleTemplate() != null;
}
@NotNull
@Override
public String getStepName() {
return "New Android Module Configuration";
}
@NotNull
@Override
protected WizardStepHeaderSettings getStepHeader() {
return getFormfactorModuleTemplate() == null
? NewModuleWizardDynamic.buildHeader()
: WizardStepHeaderSettings.createTitleAndDescriptionHeader(getFormfactorModuleTemplate().getName(), "Configure your new module");
}
@Override
public JComponent getPreferredFocusedComponent() {
return myAppName;
}
}