com.android.tools.idea.gradle.structure.DefaultSdksConfigurable.java Source code

Java tutorial

Introduction

Here is the source code for com.android.tools.idea.gradle.structure.DefaultSdksConfigurable.java

Source

/*
 * Copyright (C) 2013 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.structure;

import com.android.tools.idea.sdk.DefaultSdks;
import com.google.common.collect.Lists;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.fileChooser.FileChooser;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileChooser.PathChooserDialog;
import com.intellij.openapi.options.BaseConfigurable;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.projectRoots.JavaSdk;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.ui.DetailsComponent;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.DocumentAdapter;
import com.intellij.util.Function;
import org.jetbrains.android.actions.RunAndroidSdkManagerAction;
import org.jetbrains.android.sdk.AndroidSdkData;
import org.jetbrains.android.sdk.AndroidSdkUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import javax.swing.event.DocumentEvent;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.List;

import static com.intellij.openapi.util.io.FileUtilRt.toSystemDependentName;

/**
 * Allows the user set global Android SDK and JDK locations that are used for Gradle-based Android projects.
 */
public class DefaultSdksConfigurable extends BaseConfigurable implements ValidationAwareConfigurable {
    private static final String CHOOSE_VALID_JDK_DIRECTORY_ERR = "Please choose a valid JDK directory.";
    private static final String CHOOSE_VALID_SDK_DIRECTORY_ERR = "Please choose a valid Android SDK directory.";
    private final ConfigurableHost myHost;

    // These paths are system-dependent.
    @NotNull
    private String myOriginalJdkHomePath;
    @NotNull
    private String myOriginalSdkHomePath;

    private TextFieldWithBrowseButton mySdkLocationTextField;
    private TextFieldWithBrowseButton myJdkLocationTextField;
    private JPanel myWholePanel;

    private DetailsComponent myDetailsComponent;

    public DefaultSdksConfigurable(@Nullable ConfigurableHost host) {
        myHost = host;
        myWholePanel.setPreferredSize(new Dimension(700, 500));

        myDetailsComponent = new DetailsComponent();
        myDetailsComponent.setContent(myWholePanel);
        myDetailsComponent.setText("SDK Location");
    }

    @Override
    public void disposeUIResources() {
    }

    @Override
    public void reset() {
        myOriginalSdkHomePath = getDefaultSdkPath();
        myOriginalJdkHomePath = getDefaultJdkPath();

        mySdkLocationTextField.setText(myOriginalSdkHomePath);
        myJdkLocationTextField.setText(myOriginalJdkHomePath);
    }

    @Override
    public void apply() throws ConfigurationException {
        ApplicationManager.getApplication().runWriteAction(new Runnable() {
            @Override
            public void run() {
                DefaultSdks.setDefaultAndroidHome(getSdkLocation());
                DefaultSdks.setDefaultJavaHome(getJdkLocation());

                if (!ApplicationManager.getApplication().isUnitTestMode()) {
                    RunAndroidSdkManagerAction.updateInWelcomePage(myDetailsComponent.getComponent());
                }
            }
        });
    }

    private void createUIComponents() {
        createSdkLocationTextField();
        createJdkLocationTextField();
    }

    private void createSdkLocationTextField() {
        final FileChooserDescriptor descriptor = createSingleFolderDescriptor("Choose Android SDK Location",
                new Function<File, Void>() {
                    @Override
                    public Void fun(File file) {
                        if (!DefaultSdks.isValidAndroidSdkPath(file)) {
                            throw new IllegalArgumentException(CHOOSE_VALID_SDK_DIRECTORY_ERR);
                        }
                        return null;
                    }
                });

        JTextField textField = new JTextField(10);
        mySdkLocationTextField = new TextFieldWithBrowseButton(textField, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                VirtualFile suggestedDir = null;
                File sdkLocation = getSdkLocation();
                if (sdkLocation.isDirectory()) {
                    suggestedDir = VfsUtil.findFileByIoFile(sdkLocation, false);
                }
                VirtualFile chosen = FileChooser.chooseFile(descriptor, null, suggestedDir);
                if (chosen != null) {
                    File f = VfsUtilCore.virtualToIoFile(chosen);
                    mySdkLocationTextField.setText(f.getPath());
                }
            }
        });
        installValidationListener(textField);
    }

    private void createJdkLocationTextField() {
        final FileChooserDescriptor descriptor = createSingleFolderDescriptor("Choose JDK Location",
                new Function<File, Void>() {
                    @Override
                    public Void fun(File file) {
                        if (!JavaSdk.checkForJdk(file)) {
                            throw new IllegalArgumentException(CHOOSE_VALID_JDK_DIRECTORY_ERR);
                        }
                        return null;
                    }
                });

        JTextField textField = new JTextField(10);
        myJdkLocationTextField = new TextFieldWithBrowseButton(textField, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                VirtualFile suggestedDir = null;
                File jdkLocation = getJdkLocation();
                if (jdkLocation.isDirectory()) {
                    suggestedDir = VfsUtil.findFileByIoFile(jdkLocation, false);
                }
                VirtualFile chosen = FileChooser.chooseFile(descriptor, null, suggestedDir);
                if (chosen != null) {
                    File f = VfsUtilCore.virtualToIoFile(chosen);
                    myJdkLocationTextField.setText(f.getPath());
                }
            }
        });
        installValidationListener(textField);
    }

    private void installValidationListener(JTextField textField) {
        if (myHost != null) {
            textField.getDocument().addDocumentListener(new DocumentAdapter() {
                @Override
                protected void textChanged(DocumentEvent e) {
                    myHost.requestValidation();
                }
            });
        }
    }

    @NotNull
    private static FileChooserDescriptor createSingleFolderDescriptor(@NotNull String title,
            @NotNull final Function<File, Void> validation) {
        final FileChooserDescriptor descriptor = new FileChooserDescriptor(false, true, false, false, false,
                false) {
            @Override
            public void validateSelectedFiles(VirtualFile[] files) throws Exception {
                for (VirtualFile virtualFile : files) {
                    File file = VfsUtilCore.virtualToIoFile(virtualFile);
                    validation.fun(file);
                }
            }
        };
        if (SystemInfo.isMac) {
            descriptor.putUserData(PathChooserDialog.NATIVE_MAC_CHOOSER_SHOW_HIDDEN_FILES, Boolean.TRUE);
        }
        descriptor.setTitle(title);
        return descriptor;
    }

    @Override
    public String getDisplayName() {
        return "SDK Location";
    }

    @Override
    public String getHelpTopic() {
        return null;
    }

    @Nullable
    @Override
    public JComponent createComponent() {
        return myDetailsComponent.getComponent();
    }

    @NotNull
    public JComponent getContentPanel() {
        return myWholePanel;
    }

    @Override
    public boolean isModified() {
        return !myOriginalSdkHomePath.equals(getSdkLocation().getPath())
                || !myOriginalJdkHomePath.equals(getJdkLocation().getPath());
    }

    /**
     * Returns the first SDK it finds that matches our default naming convention. There will be several SDKs so named, one for each build
     * target installed in the SDK; which of those this method returns is not defined.
     *
     * @param create True if this method should attempt to create an SDK if one does not exist.
     * @return null if an SDK is unavailable or creation failed.
     */
    @Nullable
    private static Sdk getFirstDefaultAndroidSdk(boolean create) {
        List<Sdk> allAndroidSdks = DefaultSdks.getEligibleAndroidSdks();
        if (!allAndroidSdks.isEmpty()) {
            return allAndroidSdks.get(0);
        }
        if (!create) {
            return null;
        }
        AndroidSdkData sdkData = AndroidSdkUtils.tryToChooseAndroidSdk();
        if (sdkData == null) {
            return null;
        }
        List<Sdk> sdks = DefaultSdks.createAndroidSdksForAllTargets(sdkData.getLocation());
        return !sdks.isEmpty() ? sdks.get(0) : null;
    }

    /**
     * @return what the IDE is using as the home path for the Android SDK for new projects.
     */
    @NotNull
    private static String getDefaultSdkPath() {
        File path = DefaultSdks.getDefaultAndroidHome();
        if (path != null) {
            return path.getPath();
        }
        Sdk sdk = getFirstDefaultAndroidSdk(true);
        if (sdk != null) {
            String sdkHome = sdk.getHomePath();
            if (sdkHome != null) {
                return FileUtil.toSystemDependentName(sdkHome);
            }
        }
        return "";
    }

    /**
     * @return what the IDE is using as the home path for the JDK.
     */
    @NotNull
    private static String getDefaultJdkPath() {
        File javaHome = DefaultSdks.getDefaultJavaHome();
        return javaHome != null ? javaHome.getPath() : "";
    }

    @NotNull
    private File getJdkLocation() {
        String jdkLocation = myJdkLocationTextField.getText();
        return new File(toSystemDependentName(jdkLocation));
    }

    @NotNull
    private File getSdkLocation() {
        String sdkLocation = mySdkLocationTextField.getText();
        return new File(toSystemDependentName(sdkLocation));
    }

    @Override
    @NotNull
    public JComponent getPreferredFocusedComponent() {
        return mySdkLocationTextField.getTextField();
    }

    public boolean validate() throws ConfigurationException {
        if (!DefaultSdks.isValidAndroidSdkPath(getSdkLocation())) {
            throw new ConfigurationException(CHOOSE_VALID_SDK_DIRECTORY_ERR);
        }

        if (!JavaSdk.checkForJdk(getJdkLocation())) {
            throw new ConfigurationException(CHOOSE_VALID_JDK_DIRECTORY_ERR);
        }
        return true;
    }

    @Override
    @NotNull
    public List<ProjectConfigurationError> validateState() {
        List<ProjectConfigurationError> errors = Lists.newArrayList();

        if (!DefaultSdks.isValidAndroidSdkPath(getSdkLocation())) {
            ProjectConfigurationError error = new ProjectConfigurationError(CHOOSE_VALID_SDK_DIRECTORY_ERR,
                    mySdkLocationTextField.getTextField());
            errors.add(error);
        }

        if (!JavaSdk.checkForJdk(getJdkLocation())) {
            ProjectConfigurationError error = new ProjectConfigurationError(CHOOSE_VALID_JDK_DIRECTORY_ERR,
                    myJdkLocationTextField.getTextField());
            errors.add(error);
        }

        return errors;
    }

    /**
     * @return {@code true} if the configurable is needed: e.g. if we're missing a JDK or an Android SDK setting.
     */
    public static boolean isNeeded() {
        String jdkPath = getDefaultJdkPath();
        String sdkPath = getDefaultSdkPath();
        boolean validJdk = !jdkPath.isEmpty() && JavaSdk.checkForJdk(new File(jdkPath));
        boolean validSdk = !sdkPath.isEmpty() && DefaultSdks.isValidAndroidSdkPath(new File(sdkPath));
        return !validJdk || !validSdk;
    }
}