blob: ba6d128af738b9830af3d1a615e982f0cba7b513 [file] [log] [blame]
/*
* Copyright (C) 2014 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.project;
import com.android.tools.idea.gradle.GradleSyncState;
import com.android.tools.idea.gradle.project.subset.ProjectSubset;
import com.google.common.collect.Lists;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.externalSystem.model.DataNode;
import com.intellij.openapi.externalSystem.model.project.ModuleData;
import com.intellij.openapi.externalSystem.model.project.ProjectData;
import com.intellij.openapi.externalSystem.service.project.ExternalProjectRefreshCallback;
import com.intellij.openapi.externalSystem.util.ExternalSystemBundle;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.startup.StartupManager;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
import static com.android.tools.idea.gradle.util.Projects.open;
import static com.android.tools.idea.gradle.util.Projects.populate;
import static com.intellij.openapi.externalSystem.model.ProjectKeys.MODULE;
import static com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil.findAll;
class ProjectSetUpTask implements ExternalProjectRefreshCallback {
private static final Logger LOG = Logger.getInstance(ProjectSetUpTask.class);
@NotNull private final Project myProject;
private final boolean myProjectIsNew;
private final boolean mySelectModulesToImport;
private final boolean mySyncSkipped;
@Nullable private final GradleSyncListener mySyncListener;
ProjectSetUpTask(@NotNull Project project,
boolean projectIsNew,
boolean selectModulesToImport,
boolean syncSkipped,
@Nullable GradleSyncListener syncListener) {
myProject = project;
myProjectIsNew = projectIsNew;
mySelectModulesToImport = selectModulesToImport;
mySyncSkipped = syncSkipped;
mySyncListener = syncListener;
}
@Override
public void onSuccess(@Nullable final DataNode<ProjectData> projectInfo) {
assert projectInfo != null;
populateProject(projectInfo);
Runnable runnable = new Runnable() {
@Override
public void run() {
boolean isTest = ApplicationManager.getApplication().isUnitTestMode();
if (!isTest || !GradleProjectImporter.ourSkipSetupFromTest) {
if (myProjectIsNew) {
open(myProject);
}
if (!isTest) {
myProject.save();
}
}
if (myProjectIsNew) {
// We need to do this because AndroidGradleProjectComponent#projectOpened is being called when the project is created, instead
// of when the project is opened. When 'projectOpened' is called, the project is not fully configured, and it does not look
// like it is Gradle-based, resulting in listeners (e.g. modules added events) not being registered. Here we force the
// listeners to be registered.
AndroidGradleProjectComponent projectComponent = ServiceManager.getService(myProject, AndroidGradleProjectComponent.class);
projectComponent.configureGradleProject();
}
if (mySyncListener != null) {
if (mySyncSkipped) {
mySyncListener.syncSkipped(myProject);
}
else {
mySyncListener.syncSucceeded(myProject);
}
}
}
};
if (ApplicationManager.getApplication().isUnitTestMode()) {
runnable.run();
}
else {
UIUtil.invokeLaterIfNeeded(runnable);
}
}
private void populateProject(@NotNull final DataNode<ProjectData> projectInfo) {
StartupManager.getInstance(myProject).runWhenProjectIsInitialized(new Runnable() {
@Override
public void run() {
populate(myProject, getModulesToImport(projectInfo));
}
});
}
@NotNull
private Collection<DataNode<ModuleData>> getModulesToImport(DataNode<ProjectData> projectInfo) {
Collection<DataNode<ModuleData>> modules = findAll(projectInfo, MODULE);
ProjectSubset subview = ProjectSubset.getInstance(myProject);
if (!ApplicationManager.getApplication().isUnitTestMode() && ProjectSubset.isSettingEnabled() && modules.size() > 1) {
if (mySelectModulesToImport) {
// Importing a project. Allow user to select which modules to include in the project.
Collection<DataNode<ModuleData>> selection = subview.showModuleSelectionDialog(modules);
if (selection != null) {
return selection;
}
}
else {
// We got here because a project was synced with Gradle. Make sure that we don't add any modules that were not selected during
// project import (if applicable.)
String[] persistedModuleNames = subview.getSelection();
if (persistedModuleNames != null) {
int moduleCount = persistedModuleNames.length;
if (moduleCount > 0) {
List<String> moduleNames = Lists.newArrayList(persistedModuleNames);
List<DataNode<ModuleData>> selectedModules = Lists.newArrayListWithExpectedSize(moduleCount);
for (DataNode<ModuleData> module : modules) {
String name = module.getData().getExternalName();
if (moduleNames.contains(name)) {
selectedModules.add(module);
}
}
return selectedModules;
}
}
}
}
// Delete any stored module selection.
subview.clearSelection();
return modules;
}
@Override
public void onFailure(@NotNull String errorMessage, @Nullable String errorDetails) {
if (errorDetails != null) {
LOG.warn(errorDetails);
}
String newMessage = ExternalSystemBundle.message("error.resolve.with.reason", errorMessage);
LOG.info(newMessage);
// Remove cache data to force a sync next time the project is open. This is necessary when checking MD5s is not enough. For example,
// when sync failed because the SDK being used by the project was accidentally removed in the SDK Manager. The state of the project did
// not change, and if we don't force a sync, the project will use the cached state and it would look like there are no errors.
GradleProjectSyncData.removeFrom(myProject);
GradleSyncState.getInstance(myProject).syncFailed(newMessage);
if (mySyncListener != null) {
mySyncListener.syncFailed(myProject, newMessage);
}
}
}