blob: 890100dd724cb890792b56bee9f1893212c497a0 [file] [log] [blame]
/*
* 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.rendering;
import com.android.annotations.VisibleForTesting;
import com.android.tools.idea.gradle.project.GradleSyncListener;
import com.google.common.collect.Lists;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.project.Project;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.util.AndroidUtils;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
/** Resource repository for a module along with all its module dependencies */
public final class ProjectResourceRepository extends MultiResourceRepository {
private final AndroidFacet myFacet;
private ProjectResourceRepository(@NotNull AndroidFacet facet, @NotNull List<? extends LocalResourceRepository> delegates) {
super(facet.getModule().getName() + " with modules", delegates);
myFacet = facet;
}
/**
* Returns the Android resources for this module and any modules it depends on, but not resources in any libraries
*
* @param module the module to look up resources for
* @param createIfNecessary if true, create the app resources if necessary, otherwise only return if already computed
* @return the resource repository
*/
@Nullable
public static ProjectResourceRepository getProjectResources(@NotNull Module module, boolean createIfNecessary) {
AndroidFacet facet = AndroidFacet.getInstance(module);
if (facet != null) {
return facet.getProjectResources(createIfNecessary);
}
return null;
}
/**
* Returns the Android resources for this module and any modules it depends on, but not resources in any libraries
*
* @param facet the module facet to look up resources for
* @param createIfNecessary if true, create the app resources if necessary, otherwise only return if already computed
* @return the resource repository
*/
@Contract("!null, true -> !null")
@Nullable
public static ProjectResourceRepository getProjectResources(@NotNull AndroidFacet facet, boolean createIfNecessary) {
return facet.getProjectResources(createIfNecessary);
}
@NotNull
public static ProjectResourceRepository create(@NotNull final AndroidFacet facet) {
List<LocalResourceRepository> resources = computeRepositories(facet);
final ProjectResourceRepository repository = new ProjectResourceRepository(facet, resources);
// TODO: Avoid this in non-Gradle projects?
facet.addListener(new GradleSyncListener.Adapter() {
@Override
public void syncSucceeded(@NotNull Project project) {
// Dependencies can change when we sync with Gradle
repository.updateRoots();
}
});
return repository;
}
private static List<LocalResourceRepository> computeRepositories(@NotNull final AndroidFacet facet) {
LocalResourceRepository main = ModuleResourceRepository.getModuleResources(facet, true);
// List of module facets the given module depends on
List<AndroidFacet> dependentFacets = AndroidUtils.getAllAndroidDependencies(facet.getModule(), true);
if (dependentFacets.isEmpty()) {
return Collections.singletonList(main);
}
List<LocalResourceRepository> resources = Lists.newArrayListWithExpectedSize(dependentFacets.size());
for (AndroidFacet f : dependentFacets) {
LocalResourceRepository r = ModuleResourceRepository.getModuleResources(f, true);
resources.add(r);
}
resources.add(main);
return resources;
}
void updateRoots() {
List<LocalResourceRepository> repositories = computeRepositories(myFacet);
updateRoots(repositories);
}
@VisibleForTesting
void updateRoots(List<LocalResourceRepository> resourceDirectories) {
if (resourceDirectories.equals(myChildren)) {
// Nothing changed (including order); nothing to do
return;
}
setChildren(resourceDirectories);
}
/**
* Called when module roots have changed in the given project. Locates all
* the {@linkplain ProjectResourceRepository} instances (but only those that
* have already been initialized) and updates the roots, if necessary.
*
* @param project the project whose module roots changed.
*/
public static void moduleRootsChanged(@NotNull Project project) {
ModuleManager moduleManager = ModuleManager.getInstance(project);
for (Module module : moduleManager.getModules()) {
moduleRootsChanged(module);
}
}
/**
* Called when module roots have changed in the given module. Locates the
* {@linkplain ProjectResourceRepository} instance (but only if it has
* already been initialized) and updates its roots, if necessary.
* <p>
* TODO: Currently, this method is only called during a Gradle project import.
* We should call it for non-Gradle projects after modules are changed in the
* project structure dialog etc. with
* project.getMessageBus().connect().subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootListener() { ... }
*
* @param module the module whose roots changed
*/
private static void moduleRootsChanged(@NotNull Module module) {
AndroidFacet facet = AndroidFacet.getInstance(module);
if (facet != null) {
if (facet.requiresAndroidModel() && facet.getAndroidModel() == null) {
// Project not yet fully initialized; no need to do a sync now because our
// GradleProjectAvailableListener will be called as soon as it is and do a proper sync
return;
}
ProjectResourceRepository projectResources = getProjectResources(facet, false);
if (projectResources != null) {
projectResources.updateRoots();
AppResourceRepository appResources = AppResourceRepository.getAppResources(facet, false);
if (appResources != null) {
appResources.invalidateCache(projectResources);
appResources.updateRoots();
}
}
}
}
@VisibleForTesting
@NotNull
static ProjectResourceRepository createForTest(AndroidFacet facet, List<LocalResourceRepository> modules) {
return new ProjectResourceRepository(facet, modules);
}
}