/* * 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.util; import com.android.tools.idea.gradle.GradleSyncState; import com.android.tools.idea.gradle.JavaModel; import com.android.tools.idea.gradle.compiler.AndroidGradleBuildConfiguration; import com.android.tools.idea.gradle.facet.AndroidGradleFacet; import com.android.tools.idea.gradle.facet.JavaGradleFacet; import com.android.tools.idea.gradle.messages.ProjectSyncMessages; import com.android.tools.idea.startup.AndroidStudioSpecificInitializer; import com.intellij.ide.DataManager; import com.intellij.ide.impl.ProjectUtil; import com.intellij.ide.projectView.ProjectView; import com.intellij.ide.projectView.impl.AbstractProjectViewPane; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.actionSystem.LangDataKeys; 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.openapi.project.ex.ProjectManagerEx; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.openapi.wm.IdeFrame; import com.intellij.openapi.wm.WindowManager; import com.intellij.openapi.wm.ex.IdeFrameEx; import com.intellij.openapi.wm.impl.IdeFrameImpl; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.gradle.settings.GradleSettings; import javax.swing.*; import java.io.File; import static com.android.tools.idea.gradle.messages.CommonMessageGroupNames.VARIANT_SELECTION_CONFLICTS; /** * Utility methods for {@link Project}s. */ public final class Projects { public static final Key<Boolean> HAS_UNRESOLVED_DEPENDENCIES = Key.create("has.unresolved.dependencies"); public static final Key<Boolean> HAS_WRONG_JDK = Key.create("has.wrong.jdk"); private static final Logger LOG = Logger.getInstance(Projects.class); private static final Module[] NO_MODULES = new Module[0]; private Projects() { } /** * Indicates whether the last sync with Gradle failed. */ public static boolean lastGradleSyncFailed(@NotNull Project project) { return (!GradleSyncState.getInstance(project).isSyncInProgress() && isGradleProjectWithoutModel(project)) || hasErrors(project); } public static boolean hasErrors(@NotNull Project project) { if (hasUnresolvedDependencies(project) || hasWrongJdk(project)) { return true; } ProjectSyncMessages messages = ProjectSyncMessages.getInstance(project); int errorCount = messages.getErrorCount(); if (errorCount > 0) { return false; } // Variant selection errors do not count as "sync failed" errors. int variantSelectionErrorCount = messages.getMessageCount(VARIANT_SELECTION_CONFLICTS); return errorCount != variantSelectionErrorCount; } private static boolean hasUnresolvedDependencies(@NotNull Project project) { return getBoolean(project, HAS_UNRESOLVED_DEPENDENCIES); } private static boolean hasWrongJdk(@NotNull Project project) { return getBoolean(project, HAS_WRONG_JDK); } private static boolean getBoolean(@NotNull Project project, @NotNull Key<Boolean> key) { Boolean val = project.getUserData(key); return val != null && val.booleanValue(); } /** * Indicates the given project is an Gradle-based Android project that does not contain any Gradle model. Possible causes for this * scenario to happen are: * <ul> * <li>the last sync with Gradle failed</li> * <li>Studio just started up and it has not synced the project yet</li> * </ul> * * @param project the project. * @return {@code true} if the project is a Gradle-based Android project that does not contain any Gradle model. */ public static boolean isGradleProjectWithoutModel(@NotNull Project project) { for (Module module : ModuleManager.getInstance(project).getModules()) { AndroidFacet facet = AndroidFacet.getInstance(module); if (facet != null && facet.isGradleProject() && facet.getIdeaAndroidProject() == null) { return true; } } return false; } /** * Opens the given project in the IDE. * * @param project the project to open. */ public static void open(@NotNull Project project) { ProjectManagerEx projectManager = ProjectManagerEx.getInstanceEx(); ProjectUtil.updateLastProjectLocation(project.getBasePath()); if (WindowManager.getInstance().isFullScreenSupportedInCurrentOS()) { IdeFocusManager instance = IdeFocusManager.findInstance(); IdeFrame lastFocusedFrame = instance.getLastFocusedFrame(); if (lastFocusedFrame instanceof IdeFrameEx) { boolean fullScreen = ((IdeFrameEx)lastFocusedFrame).isInFullScreen(); if (fullScreen) { project.putUserData(IdeFrameImpl.SHOULD_OPEN_IN_FULL_SCREEN, Boolean.TRUE); } } } projectManager.openProject(project); } public static boolean isDirectGradleInvocationEnabled(@NotNull Project project) { AndroidGradleBuildConfiguration buildConfiguration = AndroidGradleBuildConfiguration.getInstance(project); return buildConfiguration.USE_EXPERIMENTAL_FASTER_BUILD; } public static boolean isOfflineBuildModeEnabled(@NotNull Project project) { return GradleSettings.getInstance(project).isOfflineWork(); } public static boolean isGradleProject(@NotNull Project project) { ModuleManager moduleManager = ModuleManager.getInstance(project); for (Module module : moduleManager.getModules()) { AndroidFacet androidFacet = AndroidFacet.getInstance(module); if (androidFacet != null && androidFacet.isGradleProject()) { return true; } } return false; } /** * Indicates whether the give project is a legacy IDEA Android project (which is deprecated in Android Studio.) * * @param project the given project. * @return {@code true} if the given project is a legacy IDEA Android project; {@code false} otherwise. */ public static boolean isIdeaAndroidProject(@NotNull Project project) { ModuleManager moduleManager = ModuleManager.getInstance(project); for (Module module : moduleManager.getModules()) { if (AndroidFacet.getInstance(module) != null && AndroidGradleFacet.getInstance(module) == null) { return true; } } return false; } /** * Ensures that "External Build" is enabled for the given Gradle-based project. External build is the type of build that delegates project * building to Gradle. * * @param project the given project. This method does not do anything if the given project is not a Gradle-based project. */ public static void enforceExternalBuild(@NotNull Project project) { if (isGradleProject(project)) { // We only enforce JPS usage when the 'android' plug-in is not being used in Android Studio. if (!AndroidStudioSpecificInitializer.isAndroidStudio()) { AndroidGradleBuildConfiguration.getInstance(project).USE_EXPERIMENTAL_FASTER_BUILD = false; } } } /** * Returns the modules to build based on the current selection in the 'Project' tool window. If the module that corresponds to the project * is selected, all the modules in such projects are returned. If there is no selection, an empty array is returned. * * @param project the given project. * @param dataContext knows the modules that are selected. If {@code null}, this method gets the {@code DataContext} from the 'Project' * tool window directly. * @return the modules to build based on the current selection in the 'Project' tool window. */ @NotNull public static Module[] getModulesToBuildFromSelection(@NotNull Project project, @Nullable DataContext dataContext) { if (dataContext == null) { ProjectView projectView = ProjectView.getInstance(project); final AbstractProjectViewPane pane = projectView.getCurrentProjectViewPane(); if (pane != null) { JComponent treeComponent = pane.getComponentToFocus(); dataContext = DataManager.getInstance().getDataContext(treeComponent); } else { return NO_MODULES; } } Module[] modules = LangDataKeys.MODULE_CONTEXT_ARRAY.getData(dataContext); if (modules != null) { if (modules.length == 1 && isProjectModule(project, modules[0])) { return ModuleManager.getInstance(project).getModules(); } return modules; } Module module = LangDataKeys.MODULE.getData(dataContext); if (module != null) { return isProjectModule(project, module) ? ModuleManager.getInstance(project).getModules() : new Module[]{module}; } return NO_MODULES; } private static boolean isProjectModule(@NotNull Project project, @NotNull Module module) { // if we got here is because we are dealing with a Gradle project, but if there is only one module selected and this module is the // module that corresponds to the project itself, it won't have an android-gradle facet. In this case we treat it as if we were going // to build the whole project. File moduleFilePath = new File(FileUtil.toSystemDependentName(module.getModuleFilePath())); File moduleRootDirPath = moduleFilePath.getParentFile(); if (moduleRootDirPath == null) { return false; } return FileUtil.filesEqual(moduleRootDirPath, new File(project.getBasePath())) && AndroidGradleFacet.getInstance(module) == null; } /** * Indicates whether Gradle is used to build this project. * Note: {@link #isGradleProject(com.intellij.openapi.project.Project)} indicates whether a project has a IdeaAndroidProject model. * That method should be preferred in almost all cases. Use this method only if you explicitly need to check whether the model was * generated by Gradle (this will exclude models generated by other build systems.) */ public static boolean isBuildWithGradle(@NotNull Project project) { ModuleManager moduleManager = ModuleManager.getInstance(project); for (Module module : moduleManager.getModules()) { if (AndroidGradleFacet.getInstance(module) != null) { return true; } } return false; } /** * @see #isGradleProjectModule(com.intellij.openapi.module.Module) */ @Nullable public static Module findGradleProjectModule(@NotNull Project project) { ModuleManager moduleManager = ModuleManager.getInstance(project); Module[] modules = moduleManager.getModules(); if (modules.length == 1) { return modules[0]; } for (Module module : modules) { if (isGradleProjectModule(module)) { return module; } } return null; } /** * Indicates whether the given module is the one that represents the project. * <p> * For example, in this project: * <pre> * project1 * - module1 * - module1.iml * - module2 * - module2.iml * -project1.iml * </pre> * "project1" is the module that represents the project. * </p> * * @param module the given module. * @return {@code true} if the given module is the one that represents the project, {@code false} otherwise. */ public static boolean isGradleProjectModule(@NotNull Module module) { AndroidFacet androidFacet = AndroidFacet.getInstance(module); if (androidFacet != null && androidFacet.isGradleProject()) { // If the module is an Android project, check that the module's path is the same as the project's. File moduleRootDirPath = new File(FileUtil.toSystemDependentName(module.getModuleFilePath())).getParentFile(); return FileUtil.pathsEqual(moduleRootDirPath.getPath(), module.getProject().getBasePath()); } // For non-Android project modules, the top-level one is the one without an "Android-Gradle" facet. return AndroidGradleFacet.getInstance(module) == null; } @Nullable public static File getBuildFolderPath(@NotNull Module module) { if (module.isDisposed() || !isGradleProject(module.getProject())) { return null; } AndroidFacet androidFacet = AndroidFacet.getInstance(module); if (androidFacet != null && androidFacet.getIdeaAndroidProject() != null) { return androidFacet.getIdeaAndroidProject().getDelegate().getBuildFolder(); } JavaGradleFacet javaFacet = JavaGradleFacet.getInstance(module); if (javaFacet != null) { JavaModel javaModel = javaFacet.getJavaModel(); if (javaModel != null) { return javaFacet.getJavaModel().getBuildFolderPath(); } String path = javaFacet.getConfiguration().BUILD_FOLDER_PATH; if (StringUtil.isNotEmpty(path)) { return new File(FileUtil.toSystemDependentName(path)); } } return null; } }