/* * 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.ResourceType; 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, ResourceType.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; } }