blob: d54a57312f51bd0ac5902d8d2197c0b2747082eb [file] [log] [blame]
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* 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 org.jetbrains.android.actions;
import com.android.resources.ResourceFolderType;
import com.intellij.CommonBundle;
import com.intellij.ide.actions.ElementCreator;
import com.intellij.ide.actions.TemplateKindCombo;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.ReadonlyStatusHandler;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.search.searches.ClassInheritorsSearch;
import com.intellij.psi.xml.XmlFile;
import com.intellij.ui.components.JBCheckBox;
import com.intellij.util.PlatformIcons;
import org.jetbrains.android.AndroidFileTemplateProvider;
import org.jetbrains.android.dom.manifest.Action;
import org.jetbrains.android.dom.manifest.*;
import org.jetbrains.android.dom.resources.ResourceValue;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.facet.AndroidRootUtil;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.android.util.AndroidResourceUtil;
import org.jetbrains.android.util.AndroidUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* @author Eugene.Kudelevsky
*/
public class NewAndroidComponentDialog extends DialogWrapper {
private JPanel myPanel;
private JLabel myKindLabel;
private JTextField myNameField;
private JLabel myUpDownHint;
private TemplateKindCombo myKindCombo;
private JTextField myLabelField;
private JCheckBox myMarkAsStartupActivityCheckBox;
private JBCheckBox myCreateLayoutFile;
private ElementCreator myCreator;
private PsiElement[] myCreatedElements;
public NewAndroidComponentDialog(@NotNull final Module module, final PsiDirectory directory) {
super(module.getProject());
myKindLabel.setLabelFor(myKindCombo);
myKindCombo.registerUpDownHint(myNameField);
myUpDownHint.setIcon(PlatformIcons.UP_DOWN_ARROWS);
myKindCombo.addItem(AndroidBundle.message("android.new.component.dialog.activity.item"), null, AndroidFileTemplateProvider.ACTIVITY);
myKindCombo.addItem(AndroidBundle.message("android.new.component.dialog.fragment.item"), null, AndroidFileTemplateProvider.FRAGMENT);
if (!containsCustomApplicationClass(module)) {
myKindCombo.addItem(AndroidBundle.message("android.new.component.dialog.application.item"), null,
AndroidFileTemplateProvider.APPLICATION);
}
myKindCombo.addItem(AndroidBundle.message("android.new.component.dialog.service.item"), null, AndroidFileTemplateProvider.SERVICE);
myKindCombo.addItem(AndroidBundle.message("android.new.component.dialog.broadcast.receiver.item"), null,
AndroidFileTemplateProvider.BROADCAST_RECEIVER);
myKindCombo.addItem(AndroidBundle.message("android.new.component.dialog.broadcast.remote.interface"), null,
AndroidFileTemplateProvider.REMOTE_INTERFACE_TEMPLATE);
init();
setTitle(AndroidBundle.message("android.new.component.action.command.name"));
myCreator = new ElementCreator(module.getProject(), CommonBundle.getErrorTitle()) {
@Override
protected PsiElement[] create(String newName) throws Exception {
final PsiElement element = NewAndroidComponentDialog.this.create(newName, directory, module.getProject());
if (element != null) {
return new PsiElement[]{element};
}
return PsiElement.EMPTY_ARRAY;
}
@Override
protected String getActionName(String newName) {
return AndroidBundle.message("android.new.component.action.command.name");
}
};
myKindCombo.getComboBox().addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String selected = myKindCombo.getSelectedName();
myMarkAsStartupActivityCheckBox.setEnabled(AndroidFileTemplateProvider.ACTIVITY.equals(selected));
myCreateLayoutFile.setEnabled(AndroidFileTemplateProvider.ACTIVITY.equals(selected) ||
AndroidFileTemplateProvider.FRAGMENT.equals(selected));
myLabelField.setEnabled(!AndroidFileTemplateProvider.REMOTE_INTERFACE_TEMPLATE.equals(selected) &&
!AndroidFileTemplateProvider.APPLICATION.equals(selected) &&
!AndroidFileTemplateProvider.FRAGMENT.equals(selected));
}
});
}
private static boolean containsCustomApplicationClass(@NotNull final Module module) {
final JavaPsiFacade facade = JavaPsiFacade.getInstance(module.getProject());
PsiClass applicationClass = ApplicationManager.getApplication().runReadAction(new Computable<PsiClass>() {
@Override
@Nullable
public PsiClass compute() {
return facade.findClass(AndroidUtils.APPLICATION_CLASS_NAME, module.getModuleWithDependenciesAndLibrariesScope(false));
}
});
return applicationClass != null && ClassInheritorsSearch.search(applicationClass, module.getModuleScope(), true).findFirst() != null;
}
@Nullable
private PsiElement create(String newName, PsiDirectory directory, Project project) throws Exception {
return doCreate(myKindCombo.getSelectedName(), directory, project, newName, myLabelField.getText(),
myMarkAsStartupActivityCheckBox.isSelected(), myCreateLayoutFile.isSelected());
}
@Nullable
static PsiElement doCreate(String templateName,
PsiDirectory directory,
Project project,
String newName,
String label,
boolean startupActivity,
boolean createLayoutFile) throws Exception {
final PsiElement element = AndroidFileTemplateProvider.createFromTemplate(templateName, newName, directory);
if (element == null) {
return null;
}
Module module = ModuleUtil.findModuleForFile(directory.getVirtualFile(), project);
if (module != null) {
final AndroidFacet facet = AndroidFacet.getInstance(module);
assert facet != null;
if (element instanceof PsiClass) {
registerComponent(templateName, (PsiClass)element, JavaDirectoryService.getInstance().getPackage(directory), facet,
label, startupActivity);
if ((AndroidFileTemplateProvider.ACTIVITY.equals(templateName) || AndroidFileTemplateProvider.FRAGMENT.equals(templateName)) &&
createLayoutFile) {
final boolean activity = AndroidFileTemplateProvider.ACTIVITY.equals(templateName);
final Manifest manifest = facet.getManifest();
final String appPackage = manifest != null ? manifest.getPackage().getValue() : null;
if (appPackage != null && appPackage.length() > 0) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
createLayoutFileForActivityOrFragment(facet, (PsiClass)element, appPackage, activity);
}
});
}
}
}
}
return element;
}
private static void createLayoutFileForActivityOrFragment(@NotNull final AndroidFacet facet,
@NotNull PsiClass activityClass,
@NotNull final String appPackage,
boolean activity) {
if (facet.isDisposed() || !activityClass.isValid()) {
return;
}
final String className = activityClass.getName();
if (className == null) {
return;
}
final XmlFile layoutFile = CreateResourceFileAction.createFileResource(facet, ResourceFolderType.LAYOUT, null, null, null, true,
"Create Layout For '" + className + "'", false);
final String layoutFileName = layoutFile != null ? layoutFile.getName() : null;
if (layoutFileName != null) {
final PsiMethod[] onCreateMethods = activityClass.findMethodsByName(activity ? "onCreate" : "onCreateView", false);
if (onCreateMethods.length != 1) {
return;
}
final PsiMethod onCreateMethod = onCreateMethods[0];
final PsiCodeBlock body = onCreateMethod.getBody();
final Project project = facet.getModule().getProject();
if (body != null) {
final String fieldName = AndroidResourceUtil.getRJavaFieldName(FileUtil.getNameWithoutExtension(layoutFileName));
final String layoutFieldRef = appPackage + ".R.layout." + fieldName;
if (activity) {
addSetContentViewStatement(body, layoutFieldRef);
}
else {
addInflateStatement(body, layoutFieldRef);
}
}
}
}
private static void addInflateStatement(final PsiCodeBlock body, final String layoutFieldRef) {
final Project project = body.getProject();
final PsiStatement[] statements = body.getStatements();
if (statements.length == 1) {
final PsiStatement statement = statements[0];
new WriteCommandAction(project, body.getContainingFile()) {
@Override
protected void run(@NotNull Result result) throws Throwable {
final PsiStatement newStatement = PsiElementFactory.SERVICE.getInstance(project).createStatementFromText(
"return inflater.inflate(" + layoutFieldRef + ", container, false);", body);
statement.replace(newStatement);
JavaCodeStyleManager.getInstance(project).shortenClassReferences(body);
CodeStyleManager.getInstance(project).reformat(body);
}
}.execute();
}
}
private static void addSetContentViewStatement(final PsiCodeBlock body, final String layoutFieldRef) {
final Project project = body.getProject();
final PsiElement lastBodyElement = body.getLastBodyElement();
if (lastBodyElement != null) {
new WriteCommandAction(project, body.getContainingFile()) {
@Override
protected void run(@NotNull Result result) throws Throwable {
final PsiStatement newStatement = PsiElementFactory.SERVICE.getInstance(project).createStatementFromText(
"setContentView(" + layoutFieldRef + ");", body);
body.addAfter(newStatement, lastBodyElement);
JavaCodeStyleManager.getInstance(project).shortenClassReferences(body);
CodeStyleManager.getInstance(project).reformat(body);
}
}.execute();
}
}
protected static void registerComponent(String templateName,
PsiClass aClass,
PsiPackage aPackage,
AndroidFacet facet,
String label,
boolean startupActivity) {
final VirtualFile manifestFile = AndroidRootUtil.getManifestFile(facet);
if (manifestFile == null ||
!ReadonlyStatusHandler.ensureFilesWritable(facet.getModule().getProject(), manifestFile)) {
return;
}
final Manifest manifest = AndroidUtils.loadDomElement(facet.getModule(), manifestFile, Manifest.class);
if (manifest == null) {
return;
}
String packageName = manifest.getPackage().getValue();
if (packageName == null || packageName.length() == 0) {
manifest.getPackage().setValue(aPackage.getQualifiedName());
}
Application application = manifest.getApplication();
if (application == null) return;
ApplicationComponent component = addToManifest(templateName, aClass, application, startupActivity);
if (component != null && label.length() > 0) {
component.getLabel().setValue(ResourceValue.literal(label));
}
}
@Nullable
protected static ApplicationComponent addToManifest(String templateName,
@NotNull PsiClass aClass,
@NotNull Application application,
boolean startupActivity) {
if (AndroidFileTemplateProvider.ACTIVITY.equals(templateName)) {
Activity activity = application.addActivity();
activity.getActivityClass().setValue(aClass);
if (startupActivity) {
IntentFilter filter = activity.addIntentFilter();
Action action = filter.addAction();
action.getName().setValue(AndroidUtils.LAUNCH_ACTION_NAME);
Category category = filter.addCategory();
category.getName().setValue(AndroidUtils.LAUNCH_CATEGORY_NAME);
}
return activity;
}
else if (AndroidFileTemplateProvider.SERVICE.equals(templateName)) {
Service service = application.addService();
service.getServiceClass().setValue(aClass);
return service;
}
else if (AndroidFileTemplateProvider.BROADCAST_RECEIVER.equals(templateName)) {
Receiver receiver = application.addReceiver();
receiver.getReceiverClass().setValue(aClass);
return receiver;
}
else if (AndroidFileTemplateProvider.APPLICATION.equals(templateName)) {
application.getName().setValue(aClass);
}
return null;
}
@Override
protected void doOKAction() {
myCreatedElements = myCreator.tryCreate(myNameField.getText());
if (myCreatedElements.length == 0) {
return;
}
super.doOKAction();
}
@Override
public JComponent getPreferredFocusedComponent() {
return myNameField;
}
@Override
protected String getHelpId() {
return "reference.new.android.component";
}
@Override
protected JComponent createCenterPanel() {
return myPanel;
}
@NotNull
public PsiElement[] getCreatedElements() {
return myCreatedElements;
}
}