com.android.tools.idea.gradle.actions.LinkExternalCppProjectDialog.java Source code

Java tutorial

Introduction

Here is the source code for com.android.tools.idea.gradle.actions.LinkExternalCppProjectDialog.java

Source

/*
 * Copyright (C) 2016 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.gradle.actions;

import com.android.tools.idea.gradle.dsl.model.GradleBuildModel;
import com.android.tools.idea.gradle.dsl.model.android.AndroidModel;
import com.android.tools.idea.gradle.project.sync.GradleSyncInvoker;
import com.google.common.base.Strings;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.*;
import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.openapi.vfs.VirtualFile;
import org.jdesktop.swingx.JXLabel;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.io.File;

import static com.intellij.openapi.util.io.FileUtil.toSystemIndependentName;
import static com.intellij.openapi.vfs.VfsUtilCore.virtualToIoFile;
import static org.jetbrains.android.facet.AndroidRootUtil.getPathRelativeToModuleDir;

public class LinkExternalCppProjectDialog extends DialogWrapper {
    private static final String CMAKE_PATH_DESCRIPTION = "Select the main CMakeLists.txt file of a CMake project";
    private static final String NDK_BUILD_PATH_DESCRIPTION = "Select the main .mk file of the NDK project (e.g. Android.mk)";

    private enum BuildSystem {
        CMAKE("CMake"), NDK_BUILD("ndk-build");

        private String myText;

        BuildSystem(String text) {
            myText = text;
        }

        public String toString() {
            return myText;
        }
    }

    @NotNull
    private final Module myModule;

    private JPanel myPanel;
    private JComboBox<BuildSystem> myBuildSystemCombo;
    private JXLabel myProjectPathDescriptionLabel;
    private TextFieldWithBrowseButton myProjectPathTextField;
    private JXLabel myProjectPathResultLabel;

    public LinkExternalCppProjectDialog(@NotNull Module module) {
        super(false);

        myModule = module;

        init();

        setTitle("Link C++ Project with Gradle");

        myBuildSystemCombo.addItem(BuildSystem.CMAKE);
        myBuildSystemCombo.addItem(BuildSystem.NDK_BUILD);

        myBuildSystemCombo.setSelectedItem(BuildSystem.CMAKE);
        myProjectPathDescriptionLabel.setText(CMAKE_PATH_DESCRIPTION);

        myBuildSystemCombo.addItemListener(e -> {
            if (myBuildSystemCombo.getSelectedItem() == BuildSystem.CMAKE) {
                myProjectPathDescriptionLabel.setText(CMAKE_PATH_DESCRIPTION);
            } else {
                myProjectPathDescriptionLabel.setText(NDK_BUILD_PATH_DESCRIPTION);
            }
        });

        getOKAction().setEnabled(false);

        myProjectPathTextField.setTextFieldPreferredWidth(50);
        FileChooserDescriptor descriptor = new FileChooserDescriptor(true, false, false, false, false, false) {
            @Override
            public void validateSelectedFiles(VirtualFile[] files) throws Exception {
                for (VirtualFile virtualFile : files) {
                    String errorMessage = validateProjectFilePath(virtualToIoFile(virtualFile));
                    if (errorMessage != null) {
                        throw new IllegalArgumentException(errorMessage);
                    }
                }
            }
        };

        descriptor.setTitle("Choose C++ Project Location");

        myProjectPathTextField.addActionListener(
                new ComponentWithBrowseButton.BrowseFolderActionListener<>("Select C++ Project Location", null,
                        myProjectPathTextField, null, descriptor, TextComponentAccessor.TEXT_FIELD_WHOLE_TEXT));

        myProjectPathResultLabel.setVisible(false);
    }

    @Override
    protected boolean postponeValidation() {
        return false;
    }

    @Nullable
    @Override
    protected ValidationInfo doValidate() {
        String projectPath = toSystemIndependentName(myProjectPathTextField.getText().trim());
        if (Strings.isNullOrEmpty(projectPath)) {
            getOKAction().setEnabled(false);
            setProjectPathResultLabelVisible(false);
            return null;
        }

        String errorMessage = validateProjectFilePath(new File(projectPath));
        if (errorMessage != null) {
            setProjectPathResultLabelVisible(false);
            return new ValidationInfo(errorMessage, myProjectPathTextField.getTextField());
        }
        String relativePath = getPathRelativeToModuleDir(myModule, projectPath);
        assert relativePath != null;

        myProjectPathResultLabel.setText(
                "<html>Path to be saved into the build.gradle file:<br><b>\"" + relativePath + "\"</b></html>");

        getOKAction().setEnabled(true);
        setProjectPathResultLabelVisible(true);

        return null;
    }

    private void setProjectPathResultLabelVisible(boolean newVisible) {
        boolean oldVisible = myProjectPathResultLabel.isVisible();
        if (oldVisible != newVisible) {
            myProjectPathResultLabel.setVisible(newVisible);

            myPanel.doLayout();
            myPanel.revalidate();
            myPanel.repaint();
        }
    }

    @Nullable
    private String validateProjectFilePath(@NotNull File file) {
        if (!file.exists()) {
            return "The selected file does not exist";
        }
        if (myBuildSystemCombo.getSelectedItem() == BuildSystem.CMAKE && !file.getName().equals("CMakeLists.txt")) {
            return "Invalid file name. Expected: CMakeLists.txt";
        } else if (myBuildSystemCombo.getSelectedItem() == BuildSystem.NDK_BUILD
                && !FileUtilRt.extensionEquals(file.getPath(), "mk")) {
            return "Invalid file extension. Expected: .mk";
        }
        return null;
    }

    @Override
    protected void doOKAction() {
        String projectPath = toSystemIndependentName(myProjectPathTextField.getText().trim());
        String relativePath = getPathRelativeToModuleDir(myModule, projectPath);
        assert relativePath != null;

        GradleBuildModel buildModel = GradleBuildModel.get(myModule);
        assert buildModel != null;

        AndroidModel android = buildModel.android();
        assert android != null;

        if (myBuildSystemCombo.getSelectedItem() == BuildSystem.CMAKE) {
            android.externalNativeBuild().cmake().setPath(new File(relativePath));
        } else {
            android.externalNativeBuild().ndkBuild().setPath(new File(relativePath));
        }

        Project project = myModule.getProject();
        new WriteCommandAction(project, "Link C++ Project with Gradle") {
            @Override
            protected void run(@NotNull Result result) throws Throwable {
                buildModel.applyChanges();
            }
        }.execute();

        GradleSyncInvoker.getInstance().requestProjectSyncAndSourceGeneration(project, null);
        super.doOKAction();
    }

    @Nullable
    @Override
    protected JComponent createCenterPanel() {
        return myPanel;
    }
}