/*
* 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.wizard;
import com.android.annotations.VisibleForTesting;
import com.google.common.base.Splitter;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.project.Project;
import com.intellij.ui.components.JBLabel;
import com.intellij.uiDesigner.core.GridConstraints;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.Set;
import static com.android.tools.idea.wizard.WizardConstants.APPLICATION_NAME_KEY;
import static com.android.tools.idea.wizard.WizardConstants.PROJECT_LOCATION_KEY;
import static com.android.tools.idea.wizard.WizardConstants.SELECTED_MODULE_TYPE_KEY;
/**
* Configuration for a new Android module
*/
public class ConfigureAndroidModuleStepDynamic extends ConfigureAndroidProjectStep {
private static final Logger LOG = Logger.getInstance(ConfigureAndroidModuleStepDynamic.class);
private CreateModuleTemplate myModuleType;
private FormFactorApiComboBox mySdkControls;
private Project myProject;
public ConfigureAndroidModuleStepDynamic(@Nullable Project project, @Nullable Disposable parentDisposable) {
super("Configure your new module", parentDisposable);
myProject = project;
}
@Override
public void init() {
String projectLocation = myState.get(PROJECT_LOCATION_KEY);
super.init();
myProjectLocation.setVisible(false);
myProjectLocationLabel.setVisible(false);
deregister(myProjectLocation);
unregisterValueDeriver(PROJECT_LOCATION_KEY);
myProjectLocation.setText(projectLocation);
myState.put(PROJECT_LOCATION_KEY, projectLocation);
}
@Override
public void onEnterStep() {
super.onEnterStep();
CreateModuleTemplate moduleType = getModuleType();
if (moduleType != null && moduleType.formFactor != null && moduleType.templateMetadata != null) {
myModuleType = moduleType;
registerValueDeriver(FormFactorUtils.getModuleNameKey(moduleType.formFactor), ourModuleNameDeriver);
if (mySdkControls != null) {
// Remove existing SDK combo if we have one
deregister(mySdkControls);
myPanel.remove(mySdkControls);
}
mySdkControls = new FormFactorApiComboBox(moduleType.formFactor, moduleType.templateMetadata.getMinSdk());
GridConstraints constraints = new GridConstraints();
constraints.setColumn(0);
constraints.setRow(3);
myPanel.add(new JBLabel(ConfigureFormFactorStep.MIN_SDK_STRING), constraints);
constraints.setColumn(1);
constraints.setFill(GridConstraints.FILL_HORIZONTAL);
myPanel.add(mySdkControls, constraints);
mySdkControls.register(this);
} else {
LOG.error("init() Called on ConfigureAndroidModuleStepDynamic with an incorrect selected ModuleType");
}
if (mySdkControls != null) {
mySdkControls.loadSavedApi();
}
invokeUpdate(null);
}
@Nullable
private CreateModuleTemplate getModuleType() {
ModuleTemplate moduleTemplate = myState.get(SELECTED_MODULE_TYPE_KEY);
if (moduleTemplate instanceof CreateModuleTemplate) {
CreateModuleTemplate type = (CreateModuleTemplate)moduleTemplate;
if (type.formFactor != null && type.templateMetadata != 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.formFactor.toString();
}
return computeModuleName(appName);
}
};
@NotNull
@VisibleForTesting
String computeModuleName(@NotNull String appName) {
String moduleName = appName.toLowerCase().replaceAll(INVALID_FILENAME_CHARS, "");
moduleName = moduleName.replaceAll("\\s", "");
if (!isUniqueModuleName(moduleName)) {
int i = 2;
while (!isUniqueModuleName(moduleName + Integer.toString(i))) {
i++;
}
moduleName += Integer.toString(i);
}
return moduleName;
}
@VisibleForTesting
static boolean isValidModuleName(@NotNull String moduleName) {
if (!moduleName.replaceAll(INVALID_FILENAME_CHARS, "").equals(moduleName)) {
return false;
}
for (String s : Splitter.on('.').split(moduleName)) {
if (INVALID_MSFT_FILENAMES.contains(s.toLowerCase())) {
return false;
}
}
return true;
}
private boolean isUniqueModuleName(@NotNull String moduleName) {
if (myProject == null) {
return true;
}
// Check our modules
ModuleManager moduleManager = ModuleManager.getInstance(myProject);
for (Module m : moduleManager.getModules()) {
if (m.getName().equalsIgnoreCase(moduleName)) {
return false;
}
}
return true;
}
@Override
public boolean isStepVisible() {
return getModuleType() != null;
}
@NotNull
@Override
public String getStepName() {
return "New Android Module Configuration";
}
@Nullable
@Override
protected JComponent getHeader() {
return null;
}
}