blob: 21d0148361bfb39f746fc5f80180fdb716511915 [file] [log] [blame]
/*
* Copyright (C) 2015 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.editors.theme;
import com.android.ide.common.resources.ResourceResolver;
import com.android.tools.idea.configurations.Configuration;
import com.android.tools.idea.configurations.ConfigurationListener;
import com.android.tools.idea.editors.theme.datamodels.ThemeEditorStyle;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* Lots of objects in theme editor require access to instance of Configuration,
* Module and other classes that provide a way to access Android-specific data.
* However, the problem with passing instances of these classes directly is that
* they have to be updated from time to time. To make it somewhat easier, an
* instance of ThemeEditorContext should be used instead.
*
* ThemeEditorContext provides instances of several classes for accessing data
* used in several classes of the theme editor. The difference between using
* ThemeEditorContext and providing these instances directly is that when
* Configuration has to be updated, it has to be done only in one place.
* It's assumed that only one instance of ThemeEditorContext should be created
* and all classes using its capabilities should retain this single instance.
*/
public class ThemeEditorContext {
// Field is initialized in method called from constructor which checker doesn't see, warning could be ignored
@SuppressWarnings("NullableProblems")
private @NotNull Configuration myConfiguration;
/**
* Stores ThemeEditorStyle currently being edited
*/
private @Nullable ThemeEditorStyle myCurrentTheme;
// Field is initialized in method called from constructor which checker doesn't see, warning could be ignored
@SuppressWarnings("NullableProblems")
private @NotNull ThemeResolver myThemeResolver;
private final List<ChangeListener> myChangeListeners = new ArrayList<ChangeListener>();
private final List<ConfigurationListener> myConfigurationListeners = new ArrayList<ConfigurationListener>();
public ThemeEditorContext(@NotNull Configuration configuration) {
setConfiguration(configuration);
}
public void updateThemeResolver() {
// setTheme(null) is required since the configuration could have a link to a non existent theme (if it was removed).
// If the configuration is pointing to a theme that does not exist anymore, the local resource resolution breaks so ThemeResolver
// fails to find the local themes.
myConfiguration.setTheme(null);
myThemeResolver = new ThemeResolver(myConfiguration);
}
@NotNull
public Configuration getConfiguration() {
return myConfiguration;
}
@NotNull
public Module getCurrentContextModule() {
return myConfiguration.getModule();
}
public void setCurrentTheme(@Nullable ThemeEditorStyle currentTheme) {
myCurrentTheme = currentTheme;
}
@Nullable
public ThemeEditorStyle getCurrentTheme() {
return myCurrentTheme;
}
/**
* Function for acquiring Module that should be used every time resource resolving
* for possible values is needed.
*/
@NotNull
public Module getModuleForResources() {
if (myCurrentTheme != null && myCurrentTheme.getSourceModule() != null) {
// If we have a source module, we want to use it for resolving possible values
return myCurrentTheme.getSourceModule();
}
// Otherwise, it should be a library theme or framework theme, in which case we will create
// a new theme in the current rendering context, which we are returning here;
return myConfiguration.getModule();
}
public void setCurrentContextModule(final @NotNull Module module) {
setConfiguration(ThemeEditorUtils.getConfigurationForModule(module));
}
@Nullable
public ResourceResolver getResourceResolver() {
return myConfiguration.getResourceResolver();
}
@NotNull
public Project getProject() {
return myConfiguration.getModule().getProject();
}
public void addConfigurationListener(@NotNull ConfigurationListener configurationListener) {
myConfigurationListeners.add(configurationListener);
myConfiguration.addListener(configurationListener);
}
@NotNull
public ThemeResolver getThemeResolver() {
return myThemeResolver;
}
public void setConfiguration(@NotNull Configuration configuration) {
for (ConfigurationListener listener : myConfigurationListeners) {
myConfiguration.removeListener(listener);
}
myConfiguration = configuration;
updateThemeResolver();
for (ConfigurationListener listener : myConfigurationListeners) {
myConfiguration.addListener(listener);
}
fireNewConfiguration();
}
public void addChangeListener(ChangeListener listener) {
myChangeListeners.add(listener);
}
private void fireNewConfiguration() {
for (ChangeListener listener : myChangeListeners) {
listener.onNewConfiguration(this);
}
}
public void dispose() {
for (ConfigurationListener listener : myConfigurationListeners) {
myConfiguration.removeListener(listener);
}
myConfigurationListeners.clear();
}
public interface ChangeListener {
void onNewConfiguration(ThemeEditorContext context);
}
}