aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationChooser.java
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationChooser.java')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationChooser.java2096
1 files changed, 0 insertions, 2096 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationChooser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationChooser.java
deleted file mode 100644
index 009b8646c..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationChooser.java
+++ /dev/null
@@ -1,2096 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
- *
- * 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.ide.eclipse.adt.internal.editors.layout.configuration;
-
-import static com.android.SdkConstants.ANDROID_NS_NAME_PREFIX;
-import static com.android.SdkConstants.ANDROID_STYLE_RESOURCE_PREFIX;
-import static com.android.SdkConstants.ATTR_CONTEXT;
-import static com.android.SdkConstants.PREFIX_RESOURCE_REF;
-import static com.android.SdkConstants.RES_QUALIFIER_SEP;
-import static com.android.SdkConstants.STYLE_RESOURCE_PREFIX;
-import static com.android.SdkConstants.TOOLS_URI;
-import static com.android.ide.eclipse.adt.AdtUtils.isUiThread;
-import static com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration.CFG_DEVICE;
-import static com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration.CFG_DEVICE_STATE;
-import static com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration.CFG_FOLDER;
-import static com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration.CFG_LOCALE;
-import static com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration.CFG_TARGET;
-import static com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration.CFG_THEME;
-import static com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration.MASK_ALL;
-import static com.google.common.base.Objects.equal;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.rendering.api.ResourceValue;
-import com.android.ide.common.rendering.api.StyleResourceValue;
-import com.android.ide.common.resources.LocaleManager;
-import com.android.ide.common.resources.ResourceFile;
-import com.android.ide.common.resources.ResourceFolder;
-import com.android.ide.common.resources.ResourceRepository;
-import com.android.ide.common.resources.configuration.DeviceConfigHelper;
-import com.android.ide.common.resources.configuration.FolderConfiguration;
-import com.android.ide.common.resources.configuration.LocaleQualifier;
-import com.android.ide.common.resources.configuration.ResourceQualifier;
-import com.android.ide.common.sdk.LoadStatus;
-import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AdtUtils;
-import com.android.ide.eclipse.adt.internal.editors.IconFactory;
-import com.android.ide.eclipse.adt.internal.editors.common.CommonXmlDelegate;
-import com.android.ide.eclipse.adt.internal.editors.common.CommonXmlEditor;
-import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
-import com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities;
-import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart;
-import com.android.ide.eclipse.adt.internal.editors.layout.gle2.IncludeFinder.Reference;
-import com.android.ide.eclipse.adt.internal.editors.layout.gle2.LayoutCanvas;
-import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo;
-import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo.ActivityAttributes;
-import com.android.ide.eclipse.adt.internal.resources.ResourceHelper;
-import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
-import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
-import com.android.ide.eclipse.adt.internal.sdk.Sdk;
-import com.android.resources.ResourceType;
-import com.android.resources.ScreenOrientation;
-import com.android.sdklib.AndroidVersion;
-import com.android.sdklib.IAndroidTarget;
-import com.android.sdklib.devices.Device;
-import com.android.sdklib.devices.DeviceManager;
-import com.android.sdklib.devices.DeviceManager.DevicesChangedListener;
-import com.android.sdklib.devices.State;
-import com.android.utils.Pair;
-import com.google.common.base.Objects;
-import com.google.common.base.Strings;
-
-import org.eclipse.core.resources.IFile;
-import org.eclipse.core.resources.IFolder;
-import org.eclipse.core.resources.IProject;
-import org.eclipse.jface.resource.ImageDescriptor;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.DisposeEvent;
-import org.eclipse.swt.events.DisposeListener;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.events.SelectionListener;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.ToolBar;
-import org.eclipse.swt.widgets.ToolItem;
-import org.eclipse.ui.IEditorPart;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.IdentityHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.SortedSet;
-
-/**
- * The {@linkplain ConfigurationChooser} allows the user to pick a
- * {@link Configuration} by configuring various constraints.
- */
-public class ConfigurationChooser extends Composite
- implements DevicesChangedListener, DisposeListener {
- private static final String ICON_SQUARE = "square"; //$NON-NLS-1$
- private static final String ICON_LANDSCAPE = "landscape"; //$NON-NLS-1$
- private static final String ICON_PORTRAIT = "portrait"; //$NON-NLS-1$
- private static final String ICON_LANDSCAPE_FLIP = "flip_landscape";//$NON-NLS-1$
- private static final String ICON_PORTRAIT_FLIP = "flip_portrait";//$NON-NLS-1$
- private static final String ICON_DISPLAY = "display"; //$NON-NLS-1$
- private static final String ICON_THEMES = "themes"; //$NON-NLS-1$
- private static final String ICON_ACTIVITY = "activity"; //$NON-NLS-1$
-
- /** The configuration state associated with this editor */
- private @NonNull Configuration mConfiguration = Configuration.create(this);
-
- /** Serialized state to use when initializing the configuration after the SDK is loaded */
- private String mInitialState;
-
- /** The client of the configuration editor */
- private final ConfigurationClient mClient;
-
- /** Counter for programmatic UI changes: if greater than 0, we're within a call */
- private int mDisableUpdates = 0;
-
- /** List of available devices */
- private Collection<Device> mDevices = Collections.emptyList();
-
- /** List of available targets */
- private final List<IAndroidTarget> mTargetList = new ArrayList<IAndroidTarget>();
-
- /** List of available themes */
- private final List<String> mThemeList = new ArrayList<String>();
-
- /** List of available locales */
- private final List<Locale > mLocaleList = new ArrayList<Locale>();
-
- /** The file being edited */
- private IFile mEditedFile;
-
- /** The {@link ProjectResources} for the edited file's project */
- private ProjectResources mResources;
-
- /** The target of the project of the file being edited. */
- private IAndroidTarget mProjectTarget;
-
- /** Dropdown for configurations */
- private ToolItem mConfigCombo;
-
- /** Dropdown for devices */
- private ToolItem mDeviceCombo;
-
- /** Dropdown for device states */
- private ToolItem mOrientationCombo;
-
- /** Dropdown for themes */
- private ToolItem mThemeCombo;
-
- /** Dropdown for locales */
- private ToolItem mLocaleCombo;
-
- /** Dropdown for activities */
- private ToolItem mActivityCombo;
-
- /** Dropdown for rendering targets */
- private ToolItem mTargetCombo;
-
- /** Whether the SDK has changed since the last model reload; if so we must reload targets */
- private boolean mSdkChanged = true;
-
- /**
- * Creates a new {@linkplain ConfigurationChooser} and adds it to the
- * parent. The method also receives custom buttons to set into the
- * configuration composite. The list is organized as an array of arrays.
- * Each array represents a group of buttons thematically grouped together.
- *
- * @param client the client embedding this configuration chooser
- * @param parent The parent composite.
- * @param initialState The initial state (serialized form) to use for the
- * configuration
- */
- public ConfigurationChooser(
- @NonNull ConfigurationClient client,
- Composite parent,
- @Nullable String initialState) {
- super(parent, SWT.NONE);
- mClient = client;
-
- setVisible(false); // Delayed until the targets are loaded
-
- mInitialState = initialState;
- setLayout(new GridLayout(1, false));
-
- IconFactory icons = IconFactory.getInstance();
-
- // TODO: Consider switching to a CoolBar instead
- ToolBar toolBar = new ToolBar(this, SWT.WRAP | SWT.FLAT | SWT.RIGHT | SWT.HORIZONTAL);
- toolBar.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
-
- mConfigCombo = new ToolItem(toolBar, SWT.DROP_DOWN );
- mConfigCombo.setImage(icons.getIcon("android_file")); //$NON-NLS-1$
- mConfigCombo.setToolTipText("Configuration to render this layout with in Eclipse");
-
- @SuppressWarnings("unused")
- ToolItem separator2 = new ToolItem(toolBar, SWT.SEPARATOR);
-
- mDeviceCombo = new ToolItem(toolBar, SWT.DROP_DOWN);
- mDeviceCombo.setImage(icons.getIcon(ICON_DISPLAY));
-
- @SuppressWarnings("unused")
- ToolItem separator3 = new ToolItem(toolBar, SWT.SEPARATOR);
-
- mOrientationCombo = new ToolItem(toolBar, SWT.DROP_DOWN);
- mOrientationCombo.setImage(icons.getIcon(ICON_PORTRAIT));
- mOrientationCombo.setToolTipText("Go to next state");
-
- @SuppressWarnings("unused")
- ToolItem separator4 = new ToolItem(toolBar, SWT.SEPARATOR);
-
- mThemeCombo = new ToolItem(toolBar, SWT.DROP_DOWN);
- mThemeCombo.setImage(icons.getIcon(ICON_THEMES));
-
- @SuppressWarnings("unused")
- ToolItem separator5 = new ToolItem(toolBar, SWT.SEPARATOR);
-
- mActivityCombo = new ToolItem(toolBar, SWT.DROP_DOWN);
- mActivityCombo.setToolTipText("Associated activity or fragment providing context");
- // The JDT class icon is lopsided, presumably because they've left room in the
- // bottom right corner for badges (for static, final etc). Unfortunately, this
- // means that the icon looks out of place when sitting close to the language globe
- // icon, the theme icon, etc so that it looks vertically misaligned:
- //mActivityCombo.setImage(JavaUI.getSharedImages().getImage(ISharedImages.IMG_OBJS_CLASS));
- // ...so use one that is centered instead:
- mActivityCombo.setImage(icons.getIcon(ICON_ACTIVITY));
-
- @SuppressWarnings("unused")
- ToolItem separator6 = new ToolItem(toolBar, SWT.SEPARATOR);
-
- //ToolBar rightToolBar = new ToolBar(this, SWT.WRAP | SWT.FLAT | SWT.RIGHT | SWT.HORIZONTAL);
- //rightToolBar.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 1, 1));
- ToolBar rightToolBar = toolBar;
-
- mLocaleCombo = new ToolItem(rightToolBar, SWT.DROP_DOWN);
- mLocaleCombo.setImage(FlagManager.getGlobeIcon());
- mLocaleCombo.setToolTipText("Locale to use when rendering layouts in Eclipse");
-
- @SuppressWarnings("unused")
- ToolItem separator7 = new ToolItem(rightToolBar, SWT.SEPARATOR);
-
- mTargetCombo = new ToolItem(rightToolBar, SWT.DROP_DOWN);
- mTargetCombo.setImage(AdtPlugin.getAndroidLogo());
- mTargetCombo.setToolTipText("Android version to use when rendering layouts in Eclipse");
-
- SelectionListener listener = new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- Object source = e.getSource();
-
- if (source == mConfigCombo) {
- ConfigurationMenuListener.show(ConfigurationChooser.this, mConfigCombo);
- } else if (source == mActivityCombo) {
- ActivityMenuListener.show(ConfigurationChooser.this, mActivityCombo);
- } else if (source == mLocaleCombo) {
- LocaleMenuListener.show(ConfigurationChooser.this, mLocaleCombo);
- } else if (source == mDeviceCombo) {
- DeviceMenuListener.show(ConfigurationChooser.this, mDeviceCombo);
- } else if (source == mTargetCombo) {
- TargetMenuListener.show(ConfigurationChooser.this, mTargetCombo);
- } else if (source == mThemeCombo) {
- ThemeMenuAction.showThemeMenu(ConfigurationChooser.this, mThemeCombo,
- mThemeList);
- } else if (source == mOrientationCombo) {
- if (e.detail == SWT.ARROW) {
- OrientationMenuAction.showMenu(ConfigurationChooser.this,
- mOrientationCombo);
- } else {
- gotoNextState();
- }
- }
- }
- };
- mConfigCombo.addSelectionListener(listener);
- mActivityCombo.addSelectionListener(listener);
- mLocaleCombo.addSelectionListener(listener);
- mDeviceCombo.addSelectionListener(listener);
- mTargetCombo.addSelectionListener(listener);
- mThemeCombo.addSelectionListener(listener);
- mOrientationCombo.addSelectionListener(listener);
-
- addDisposeListener(this);
-
- initDevices();
- initTargets();
- }
-
- /**
- * Returns the edited file
- *
- * @return the file
- */
- @Nullable
- public IFile getEditedFile() {
- return mEditedFile;
- }
-
- /**
- * Returns the project of the edited file
- *
- * @return the project
- */
- @Nullable
- public IProject getProject() {
- if (mEditedFile != null) {
- return mEditedFile.getProject();
- } else {
- return null;
- }
- }
-
- ConfigurationClient getClient() {
- return mClient;
- }
-
- /**
- * Returns the project resources for the project being configured by this
- * chooser
- *
- * @return the project resources
- */
- @Nullable
- public ProjectResources getResources() {
- return mResources;
- }
-
- /**
- * Returns the full, complete {@link FolderConfiguration}
- *
- * @return the full configuration
- */
- public FolderConfiguration getFullConfiguration() {
- return mConfiguration.getFullConfig();
- }
-
- /**
- * Returns the project target
- *
- * @return the project target
- */
- public IAndroidTarget getProjectTarget() {
- return mProjectTarget;
- }
-
- /**
- * Returns the configuration being edited by this {@linkplain ConfigurationChooser}
- *
- * @return the configuration
- */
- public Configuration getConfiguration() {
- return mConfiguration;
- }
-
- /**
- * Returns the list of locales
- * @return a list of {@link ResourceQualifier} pairs
- */
- @NonNull
- public List<Locale> getLocaleList() {
- return mLocaleList;
- }
-
- /**
- * Returns the list of available devices
- *
- * @return a list of {@link Device} objects
- */
- @NonNull
- public Collection<Device> getDevices() {
- return mDevices;
- }
-
- /**
- * Returns the list of available render targets
- *
- * @return a list of {@link IAndroidTarget} objects
- */
- @NonNull
- public List<IAndroidTarget> getTargetList() {
- return mTargetList;
- }
-
- // ---- Configuration State Lookup ----
-
- /**
- * Returns the rendering target to be used
- *
- * @return the target
- */
- @NonNull
- public IAndroidTarget getTarget() {
- IAndroidTarget target = mConfiguration.getTarget();
- if (target == null) {
- target = mProjectTarget;
- }
-
- return target;
- }
-
- /**
- * Returns the current device string, or null if no device is selected
- *
- * @return the device name, or null
- */
- @Nullable
- public String getDeviceName() {
- Device device = mConfiguration.getDevice();
- if (device != null) {
- return device.getName();
- }
-
- return null;
- }
-
- /**
- * Returns the current theme, or null if none has been selected
- *
- * @return the theme name, or null
- */
- @Nullable
- public String getThemeName() {
- String theme = mConfiguration.getTheme();
- if (theme != null) {
- theme = ResourceHelper.styleToTheme(theme);
- }
-
- return theme;
- }
-
- /** Move to the next device state, changing the icon if it changes orientation */
- private void gotoNextState() {
- State state = mConfiguration.getDeviceState();
- State flipped = mConfiguration.getNextDeviceState(state);
- if (flipped != state) {
- selectDeviceState(flipped);
- onDeviceConfigChange();
- }
- }
-
- // ---- Implements DisposeListener ----
-
- @Override
- public void widgetDisposed(DisposeEvent e) {
- dispose();
- }
-
- @Override
- public void dispose() {
- if (!isDisposed()) {
- super.dispose();
-
- final Sdk sdk = Sdk.getCurrent();
- if (sdk != null) {
- DeviceManager manager = sdk.getDeviceManager();
- manager.unregisterListener(this);
- }
- }
- }
-
- // ---- Init and reset/reload methods ----
-
- /**
- * Sets the reference to the file being edited.
- * <p/>The UI is initialized in {@link #onXmlModelLoaded()} which is called as the XML model is
- * loaded (or reloaded as the SDK/target changes).
- *
- * @param file the file being opened
- *
- * @see #onXmlModelLoaded()
- * @see #replaceFile(IFile)
- * @see #changeFileOnNewConfig(IFile)
- */
- public void setFile(IFile file) {
- mEditedFile = file;
- ensureInitialized();
- }
-
- /**
- * Replaces the UI with a given file configuration. This is meant to answer the user
- * explicitly opening a different version of the same layout from the Package Explorer.
- * <p/>This attempts to keep the current config, but may change it if it's not compatible or
- * not the best match
- * @param file the file being opened.
- */
- public void replaceFile(IFile file) {
- // if there is no previous selection, revert to default mode.
- if (mConfiguration.getDevice() == null) {
- setFile(file); // onTargetChanged will be called later.
- return;
- }
-
- setFile(file);
- IProject project = mEditedFile.getProject();
- mResources = ResourceManager.getInstance().getProjectResources(project);
-
- ResourceFolder resFolder = ResourceManager.getInstance().getResourceFolder(file);
- mConfiguration.setEditedConfig(resFolder.getConfiguration());
-
- mDisableUpdates++; // we do not want to trigger onXXXChange when setting
- // new values in the widgets.
-
- try {
- // only attempt to do anything if the SDK and targets are loaded.
- LoadStatus sdkStatus = AdtPlugin.getDefault().getSdkLoadStatus();
-
- if (sdkStatus == LoadStatus.LOADED) {
- setVisible(true);
-
- LoadStatus targetStatus = Sdk.getCurrent().checkAndLoadTargetData(mProjectTarget,
- null /*project*/);
-
- if (targetStatus == LoadStatus.LOADED) {
-
- // update the current config selection to make sure it's
- // compatible with the new file
- ConfigurationMatcher matcher = new ConfigurationMatcher(this);
- matcher.adaptConfigSelection(true /*needBestMatch*/);
- mConfiguration.syncFolderConfig();
-
- // update the string showing the config value
- selectConfiguration(mConfiguration.getEditedConfig());
- updateActivity();
- }
- } else if (sdkStatus == LoadStatus.FAILED) {
- setVisible(true);
- }
- } finally {
- mDisableUpdates--;
- }
- }
-
- /**
- * Updates the UI with a new file that was opened in response to a config change.
- * @param file the file being opened.
- *
- * @see #replaceFile(IFile)
- */
- public void changeFileOnNewConfig(IFile file) {
- setFile(file);
- IProject project = mEditedFile.getProject();
- mResources = ResourceManager.getInstance().getProjectResources(project);
-
- ResourceFolder resFolder = ResourceManager.getInstance().getResourceFolder(file);
- FolderConfiguration config = resFolder.getConfiguration();
- mConfiguration.setEditedConfig(config);
-
- // All that's needed is to update the string showing the config value
- // (since the config combo settings chosen by the user).
- selectConfiguration(config);
- }
-
- /**
- * Resets the configuration chooser to reflect the given file configuration. This is
- * intended to be used by the "Show Included In" functionality where the user has
- * picked a non-default configuration (such as a particular landscape layout) and the
- * configuration chooser must be switched to a landscape layout. This method will
- * trigger a model change.
- * <p>
- * This will NOT trigger a redraw event!
- * <p>
- * FIXME: We are currently setting the configuration file to be the configuration for
- * the "outer" (the including) file, rather than the inner file, which is the file the
- * user is actually editing. We need to refine this, possibly with a way for the user
- * to choose which configuration they are editing. And in particular, we should be
- * filtering the configuration chooser to only show options in the outer configuration
- * that are compatible with the inner included file.
- *
- * @param file the file to be configured
- */
- public void resetConfigFor(IFile file) {
- setFile(file);
-
- IFolder parent = (IFolder) mEditedFile.getParent();
- ResourceFolder resFolder = mResources.getResourceFolder(parent);
- if (resFolder != null) {
- mConfiguration.setEditedConfig(resFolder.getConfiguration());
- } else {
- FolderConfiguration config = FolderConfiguration.getConfig(
- parent.getName().split(RES_QUALIFIER_SEP));
- if (config != null) {
- mConfiguration.setEditedConfig(config);
- } else {
- mConfiguration.setEditedConfig(new FolderConfiguration());
- }
- }
-
- onXmlModelLoaded();
- }
-
-
- /**
- * Sets the current configuration to match the given folder configuration,
- * the given theme name, the given device and device state.
- *
- * @param configuration new folder configuration to use
- */
- public void setConfiguration(@NonNull Configuration configuration) {
- if (mClient != null) {
- mClient.aboutToChange(MASK_ALL);
- }
-
- Configuration oldConfiguration = mConfiguration;
- mConfiguration = configuration;
- mConfiguration.setChooser(this);
-
- selectTheme(configuration.getTheme());
- selectLocale(configuration.getLocale());
- selectDevice(configuration.getDevice());
- selectDeviceState(configuration.getDeviceState());
- selectTarget(configuration.getTarget());
- selectActivity(configuration.getActivity());
-
- // This may be a second refresh after triggered by theme above
- if (mClient != null) {
- LayoutCanvas canvas = mClient.getCanvas();
- if (canvas != null) {
- assert mConfiguration != oldConfiguration;
- canvas.getPreviewManager().updateChooserConfig(oldConfiguration, mConfiguration);
- }
-
- boolean accepted = mClient.changed(MASK_ALL);
- if (!accepted) {
- configuration = oldConfiguration;
- selectTheme(configuration.getTheme());
- selectLocale(configuration.getLocale());
- selectDevice(configuration.getDevice());
- selectDeviceState(configuration.getDeviceState());
- selectTarget(configuration.getTarget());
- selectActivity(configuration.getActivity());
- if (canvas != null && mConfiguration != oldConfiguration) {
- canvas.getPreviewManager().updateChooserConfig(mConfiguration,
- oldConfiguration);
- }
- return;
- } else {
- int changed = 0;
- if (!equal(oldConfiguration.getTheme(), mConfiguration.getTheme())) {
- changed |= CFG_THEME;
- }
- if (!equal(oldConfiguration.getDevice(), mConfiguration.getDevice())) {
- changed |= CFG_DEVICE | CFG_DEVICE_STATE;
- }
- if (changed != 0) {
- syncToVariations(changed, mEditedFile, mConfiguration, false, true);
- }
- }
- }
-
- saveConstraints();
- }
-
- /**
- * Responds to the event that the basic SDK information finished loading.
- * @param target the possibly new target object associated with the file being edited (in case
- * the SDK path was changed).
- */
- public void onSdkLoaded(IAndroidTarget target) {
- // a change to the SDK means that we need to check for new/removed devices.
- mSdkChanged = true;
-
- // store the new target.
- mProjectTarget = target;
-
- mDisableUpdates++; // we do not want to trigger onXXXChange when setting
- // new values in the widgets.
- try {
- updateDevices();
- updateTargets();
- ensureInitialized();
- } finally {
- mDisableUpdates--;
- }
- }
-
- /**
- * Responds to the XML model being loaded, either the first time or when the
- * Target/SDK changes.
- * <p>
- * This initializes the UI, either with the first compatible configuration
- * found, or it will attempt to restore a configuration if one is found to
- * have been saved in the file persistent storage.
- * <p>
- * If the SDK or target are not loaded, nothing will happen (but the method
- * must be called back when they are.)
- * <p>
- * The method automatically handles being called the first time after editor
- * creation, or being called after during SDK/Target changes (as long as
- * {@link #onSdkLoaded(IAndroidTarget)} is properly called).
- *
- * @return the target data for the rendering target used to render the
- * layout
- *
- * @see #saveConstraints()
- * @see #onSdkLoaded(IAndroidTarget)
- */
- public AndroidTargetData onXmlModelLoaded() {
- AndroidTargetData targetData = null;
-
- // only attempt to do anything if the SDK and targets are loaded.
- LoadStatus sdkStatus = AdtPlugin.getDefault().getSdkLoadStatus();
- if (sdkStatus == LoadStatus.LOADED) {
- mDisableUpdates++; // we do not want to trigger onXXXChange when setting
-
- try {
- // init the devices if needed (new SDK or first time going through here)
- if (mSdkChanged) {
- updateDevices();
- updateTargets();
- ensureInitialized();
- mSdkChanged = false;
- }
-
- IProject project = mEditedFile.getProject();
-
- Sdk currentSdk = Sdk.getCurrent();
- if (currentSdk != null) {
- mProjectTarget = currentSdk.getTarget(project);
- }
-
- LoadStatus targetStatus = LoadStatus.FAILED;
- if (mProjectTarget != null) {
- targetStatus = Sdk.getCurrent().checkAndLoadTargetData(mProjectTarget, null);
- updateTargets();
- ensureInitialized();
- }
-
- if (targetStatus == LoadStatus.LOADED) {
- setVisible(true);
- if (mResources == null) {
- mResources = ResourceManager.getInstance().getProjectResources(project);
- }
- if (mConfiguration.getEditedConfig() == null) {
- IFolder parent = (IFolder) mEditedFile.getParent();
- ResourceFolder resFolder = mResources.getResourceFolder(parent);
- if (resFolder != null) {
- mConfiguration.setEditedConfig(resFolder.getConfiguration());
- } else {
- FolderConfiguration config = FolderConfiguration.getConfig(
- parent.getName().split(RES_QUALIFIER_SEP));
- if (config != null) {
- mConfiguration.setEditedConfig(config);
- } else {
- mConfiguration.setEditedConfig(new FolderConfiguration());
- }
- }
- }
-
- targetData = Sdk.getCurrent().getTargetData(mProjectTarget);
-
- // get the file stored state
- ensureInitialized();
- boolean loadedConfigData = mConfiguration.getDevice() != null &&
- mConfiguration.getDeviceState() != null;
-
- // Load locale list. This must be run after we initialize the
- // configuration above, since it attempts to sync the UI with
- // the value loaded into the configuration.
- updateLocales();
-
- // If the current state was loaded from the persistent storage, we update the
- // UI with it and then try to adapt it (which will handle incompatible
- // configuration).
- // Otherwise, just look for the first compatible configuration.
- ConfigurationMatcher matcher = new ConfigurationMatcher(this);
- if (loadedConfigData) {
- // first make sure we have the config to adapt
- selectDevice(mConfiguration.getDevice());
- selectDeviceState(mConfiguration.getDeviceState());
- mConfiguration.syncFolderConfig();
-
- matcher.adaptConfigSelection(false);
-
- IAndroidTarget target = mConfiguration.getTarget();
- selectTarget(target);
- targetData = Sdk.getCurrent().getTargetData(target);
- } else {
- matcher.findAndSetCompatibleConfig(false);
-
- // Default to modern layout lib
- IAndroidTarget target = ConfigurationMatcher.findDefaultRenderTarget(this);
- if (target != null) {
- targetData = Sdk.getCurrent().getTargetData(target);
- selectTarget(target);
- mConfiguration.setTarget(target, true);
- }
- }
-
- // Update activity: This is done before updateThemes() since
- // the themes selection can depend on the currently selected activity
- // (e.g. when there are manifest registrations for the theme to use
- // for a given activity)
- updateActivity();
-
- // Update themes. This is done after updating the devices above,
- // since we want to look at the chosen device size to decide
- // what the default theme (for example, with Honeycomb we choose
- // Holo as the default theme but only if the screen size is XLARGE
- // (and of course only if the manifest does not specify another
- // default theme).
- updateThemes();
-
- // update the string showing the config value
- selectConfiguration(mConfiguration.getEditedConfig());
-
- // compute the final current config
- mConfiguration.syncFolderConfig();
- } else if (targetStatus == LoadStatus.FAILED) {
- setVisible(true);
- }
- } finally {
- mDisableUpdates--;
- }
- }
-
- return targetData;
- }
-
- /**
- * This is a temporary workaround for a infrequently happening bug; apparently
- * there are cases where the configuration chooser isn't shown
- */
- public void ensureVisible() {
- if (!isVisible()) {
- LoadStatus sdkStatus = AdtPlugin.getDefault().getSdkLoadStatus();
- if (sdkStatus == LoadStatus.LOADED) {
- onXmlModelLoaded();
- }
- }
- }
-
- /**
- * An alternate layout for this layout has been created. This means that the
- * current layout may no longer be a best fit. However, since we support multiple
- * layouts being open at the same time, we need to adjust the current configuration
- * back to something where this layout <b>is</b> a best match.
- */
- public void onAlternateLayoutCreated() {
- IFile best = ConfigurationMatcher.getBestFileMatch(this);
- if (best != null && !best.equals(mEditedFile)) {
- ConfigurationMatcher matcher = new ConfigurationMatcher(this);
- matcher.adaptConfigSelection(true /*needBestMatch*/);
- mConfiguration.syncFolderConfig();
- if (mClient != null) {
- mClient.changed(MASK_ALL);
- }
- }
- }
-
- /**
- * Loads the list of {@link Device}s and inits the UI with it.
- */
- private void initDevices() {
- final Sdk sdk = Sdk.getCurrent();
- if (sdk != null) {
- DeviceManager manager = sdk.getDeviceManager();
- // This method can be called more than once, so avoid duplicate entries
- manager.unregisterListener(this);
- manager.registerListener(this);
- mDevices = manager.getDevices(DeviceManager.ALL_DEVICES);
- } else {
- mDevices = new ArrayList<Device>();
- }
- }
-
- /**
- * Loads the list of {@link IAndroidTarget} and inits the UI with it.
- */
- private boolean initTargets() {
- mTargetList.clear();
-
- Sdk currentSdk = Sdk.getCurrent();
- if (currentSdk != null) {
- IAndroidTarget[] targets = currentSdk.getTargets();
- for (int i = 0 ; i < targets.length; i++) {
- if (targets[i].hasRenderingLibrary()) {
- mTargetList.add(targets[i]);
- }
- }
-
- return true;
- }
-
- return false;
- }
-
- /** Ensures that the configuration has been initialized */
- public void ensureInitialized() {
- if (mConfiguration.getDevice() == null && mEditedFile != null) {
- String data = ConfigurationDescription.getDescription(mEditedFile);
- if (mInitialState != null) {
- data = mInitialState;
- mInitialState = null;
- }
- if (data != null) {
- mConfiguration.initialize(data);
- mConfiguration.syncFolderConfig();
- }
- }
- }
-
- private void updateDevices() {
- if (mDevices.size() == 0) {
- initDevices();
- }
- }
-
- private void updateTargets() {
- if (mTargetList.size() == 0) {
- if (!initTargets()) {
- return;
- }
- }
-
- IAndroidTarget renderingTarget = mConfiguration.getTarget();
-
- IAndroidTarget match = null;
- for (IAndroidTarget target : mTargetList) {
- if (renderingTarget != null) {
- // use equals because the rendering could be from a previous SDK, so
- // it may not be the same instance.
- if (renderingTarget.equals(target)) {
- match = target;
- }
- } else if (mProjectTarget == target) {
- match = target;
- }
-
- }
-
- if (match == null) {
- // the rendering target is the same as the project.
- renderingTarget = mProjectTarget;
- } else {
- // set the rendering target to the new object.
- renderingTarget = match;
- }
-
- mConfiguration.setTarget(renderingTarget, true);
- selectTarget(renderingTarget);
- }
-
- /** Update the toolbar whenever a label has changed, to not only
- * cause the layout in the current toolbar to update, but to possibly
- * wrap the toolbars and update the layout of the surrounding area.
- */
- private void resizeToolBar() {
- Point size = getSize();
- Point newSize = computeSize(size.x, SWT.DEFAULT, true);
- setSize(newSize);
- Composite parent = getParent();
- parent.layout();
- parent.redraw();
- }
-
-
- Image getOrientationIcon(ScreenOrientation orientation, boolean flip) {
- IconFactory icons = IconFactory.getInstance();
- switch (orientation) {
- case LANDSCAPE:
- return icons.getIcon(flip ? ICON_LANDSCAPE_FLIP : ICON_LANDSCAPE);
- case SQUARE:
- return icons.getIcon(ICON_SQUARE);
- case PORTRAIT:
- default:
- return icons.getIcon(flip ? ICON_PORTRAIT_FLIP : ICON_PORTRAIT);
- }
- }
-
- ImageDescriptor getOrientationImage(ScreenOrientation orientation, boolean flip) {
- IconFactory icons = IconFactory.getInstance();
- switch (orientation) {
- case LANDSCAPE:
- return icons.getImageDescriptor(flip ? ICON_LANDSCAPE_FLIP : ICON_LANDSCAPE);
- case SQUARE:
- return icons.getImageDescriptor(ICON_SQUARE);
- case PORTRAIT:
- default:
- return icons.getImageDescriptor(flip ? ICON_PORTRAIT_FLIP : ICON_PORTRAIT);
- }
- }
-
- @NonNull
- ScreenOrientation getOrientation(State state) {
- FolderConfiguration config = DeviceConfigHelper.getFolderConfig(state);
- ScreenOrientation orientation = null;
- if (config != null && config.getScreenOrientationQualifier() != null) {
- orientation = config.getScreenOrientationQualifier().getValue();
- }
-
- if (orientation == null) {
- orientation = ScreenOrientation.PORTRAIT;
- }
-
- return orientation;
- }
-
- /**
- * Stores the current config selection into the edited file such that we can
- * bring it back the next time this layout is opened.
- */
- public void saveConstraints() {
- String description = mConfiguration.toPersistentString();
- if (description != null && !description.isEmpty()) {
- ConfigurationDescription.setDescription(mEditedFile, description);
- }
- }
-
- // ---- Setting the current UI state ----
-
- void selectDeviceState(@Nullable State state) {
- assert isUiThread();
- try {
- mDisableUpdates++;
- mOrientationCombo.setData(state);
-
- State nextState = mConfiguration.getNextDeviceState(state);
- mOrientationCombo.setImage(getOrientationIcon(getOrientation(state),
- nextState != state));
- } finally {
- mDisableUpdates--;
- }
- }
-
- void selectTarget(IAndroidTarget target) {
- assert isUiThread();
- try {
- mDisableUpdates++;
- mTargetCombo.setData(target);
- String label = getRenderingTargetLabel(target, true);
- mTargetCombo.setText(label);
- resizeToolBar();
- } finally {
- mDisableUpdates--;
- }
- }
-
- /**
- * Selects a given {@link Device} in the device combo, if it is found.
- * @param device the device to select
- * @return true if the device was found.
- */
- boolean selectDevice(@Nullable Device device) {
- assert isUiThread();
- try {
- mDisableUpdates++;
- mDeviceCombo.setData(device);
- if (device != null) {
- mDeviceCombo.setText(getDeviceLabel(device, true));
- } else {
- mDeviceCombo.setText("Device");
- }
- resizeToolBar();
- } finally {
- mDisableUpdates--;
- }
-
- return false;
- }
-
- void selectActivity(@Nullable String fqcn) {
- assert isUiThread();
- try {
- mDisableUpdates++;
- if (fqcn != null) {
- mActivityCombo.setData(fqcn);
- String label = getActivityLabel(fqcn, true);
- mActivityCombo.setText(label);
- } else {
- mActivityCombo.setText("(Select)");
- }
- resizeToolBar();
- } finally {
- mDisableUpdates--;
- }
- }
-
- void selectTheme(@Nullable String theme) {
- assert isUiThread();
- try {
- mDisableUpdates++;
- assert theme == null || theme.startsWith(STYLE_RESOURCE_PREFIX)
- || theme.startsWith(ANDROID_STYLE_RESOURCE_PREFIX) : theme;
- mThemeCombo.setData(theme);
- if (theme != null) {
- mThemeCombo.setText(getThemeLabel(theme, true));
- } else {
- // FIXME eclipse claims this is dead code.
- mThemeCombo.setText("(Set Theme)");
- }
- resizeToolBar();
- } finally {
- mDisableUpdates--;
- }
- }
-
- void selectLocale(@Nullable Locale locale) {
- assert isUiThread();
- try {
- mDisableUpdates++;
- mLocaleCombo.setData(locale);
- String label = Strings.nullToEmpty(getLocaleLabel(this, locale, true));
- mLocaleCombo.setText(label);
-
- Image image = getFlagImage(locale);
- mLocaleCombo.setImage(image);
-
- resizeToolBar();
- } finally {
- mDisableUpdates--;
- }
- }
-
- @NonNull
- Image getFlagImage(@Nullable Locale locale) {
- if (locale != null) {
- return locale.getFlagImage();
- }
-
- return FlagManager.getGlobeIcon();
- }
-
- private void selectConfiguration(FolderConfiguration fileConfig) {
- /* For now, don't show any text in the configuration combo, use just an
- icon. This has the advantage that the configuration contents don't
- shift around, so you can for example click back and forth between
- portrait and landscape without the icon moving under the mouse.
- If this works well, remove this whole method post ADT 21.
- assert isUiThread();
- try {
- String current = mEditedFile.getParent().getName();
- if (current.equals(FD_RES_LAYOUT)) {
- current = "default";
- }
-
- // Pretty things up a bit
- //if (current == null || current.equals("default")) {
- // current = "Default Configuration";
- //}
- mConfigCombo.setText(current);
- resizeToolBar();
- } finally {
- mDisableUpdates--;
- }
- */
- }
-
- /**
- * Finds a locale matching the config from a file.
- *
- * @param language the language qualifier or null if none is set.
- * @param region the region qualifier or null if none is set.
- * @return true if there was a change in the combobox as a result of
- * applying the locale
- */
- private boolean setLocale(@Nullable Locale locale) {
- boolean changed = !Objects.equal(mConfiguration.getLocale(), locale);
- selectLocale(locale);
-
- return changed;
- }
-
- // ---- Creating UI labels ----
-
- /**
- * Returns a suitable label to use to display the given activity
- *
- * @param fqcn the activity class to look up a label for
- * @param brief if true, generate a brief label (suitable for a toolbar
- * button), otherwise a fuller name (suitable for a menu item)
- * @return the label
- */
- public static String getActivityLabel(String fqcn, boolean brief) {
- if (brief) {
- String label = fqcn;
- int packageIndex = label.lastIndexOf('.');
- if (packageIndex != -1) {
- label = label.substring(packageIndex + 1);
- }
- int innerClass = label.lastIndexOf('$');
- if (innerClass != -1) {
- label = label.substring(innerClass + 1);
- }
-
- // Also strip out the "Activity" or "Fragment" common suffix
- // if this is a long name
- if (label.endsWith("Activity") && label.length() > 8 + 12) { // 12 chars + 8 in suffix
- label = label.substring(0, label.length() - 8);
- } else if (label.endsWith("Fragment") && label.length() > 8 + 12) {
- label = label.substring(0, label.length() - 8);
- }
-
- return label;
- }
-
- return fqcn;
- }
-
- /**
- * Returns a suitable label to use to display the given theme
- *
- * @param theme the theme to produce a label for
- * @param brief if true, generate a brief label (suitable for a toolbar
- * button), otherwise a fuller name (suitable for a menu item)
- * @return the label
- */
- public static String getThemeLabel(String theme, boolean brief) {
- theme = ResourceHelper.styleToTheme(theme);
-
- if (brief) {
- int index = theme.lastIndexOf('.');
- if (index < theme.length() - 1) {
- return theme.substring(index + 1);
- }
- }
- return theme;
- }
-
- /**
- * Returns a suitable label to use to display the given rendering target
- *
- * @param target the target to produce a label for
- * @param brief if true, generate a brief label (suitable for a toolbar
- * button), otherwise a fuller name (suitable for a menu item)
- * @return the label
- */
- public static String getRenderingTargetLabel(IAndroidTarget target, boolean brief) {
- if (target == null) {
- return "<null>";
- }
-
- AndroidVersion version = target.getVersion();
-
- if (brief) {
- if (target.isPlatform()) {
- return Integer.toString(version.getApiLevel());
- } else {
- return target.getName() + ':' + Integer.toString(version.getApiLevel());
- }
- }
-
- String label = String.format("API %1$d: %2$s",
- version.getApiLevel(),
- target.getShortClasspathName());
-
- return label;
- }
-
- /**
- * Returns a suitable label to use to display the given device
- *
- * @param device the device to produce a label for
- * @param brief if true, generate a brief label (suitable for a toolbar
- * button), otherwise a fuller name (suitable for a menu item)
- * @return the label
- */
- public static String getDeviceLabel(@Nullable Device device, boolean brief) {
- if (device == null) {
- return "";
- }
- String name = device.getName();
-
- if (brief) {
- // Produce a really brief summary of the device name, suitable for
- // use in the narrow space available in the toolbar for example
- int nexus = name.indexOf("Nexus"); //$NON-NLS-1$
- if (nexus != -1) {
- int begin = name.indexOf('(');
- if (begin != -1) {
- begin++;
- int end = name.indexOf(')', begin);
- if (end != -1) {
- return name.substring(begin, end).trim();
- }
- }
- }
- }
-
- return name;
- }
-
- /**
- * Returns a suitable label to use to display the given locale
- *
- * @param chooser the chooser, if known
- * @param locale the locale to look up a label for
- * @param brief if true, generate a brief label (suitable for a toolbar
- * button), otherwise a fuller name (suitable for a menu item)
- * @return the label
- */
- @Nullable
- public static String getLocaleLabel(
- @Nullable ConfigurationChooser chooser,
- @Nullable Locale locale,
- boolean brief) {
- if (locale == null) {
- return null;
- }
-
- if (!locale.hasLanguage()) {
- if (brief) {
- // Just use the icon
- return "";
- }
-
- boolean hasLocale = false;
- ResourceRepository projectRes = chooser != null ? chooser.mClient.getProjectResources()
- : null;
- if (projectRes != null) {
- hasLocale = projectRes.getLanguages().size() > 0;
- }
-
- if (hasLocale) {
- return "Other";
- } else {
- return "Any";
- }
- }
-
- String languageCode = locale.qualifier.getLanguage();
- String languageName = LocaleManager.getLanguageName(languageCode);
-
- if (!locale.hasRegion()) {
- // TODO: Make the region string use "Other" instead of "Any" if
- // there is more than one region for a given language
- //if (regions.size() > 0) {
- // return String.format("%1$s / Other", language);
- //} else {
- // return String.format("%1$s / Any", language);
- //}
- if (!brief && languageName != null) {
- return String.format("%1$s (%2$s)", languageName, languageCode);
- } else {
- return languageCode;
- }
- } else {
- String regionCode = locale.qualifier.getRegion();
- if (!brief && languageName != null) {
- String regionName = LocaleManager.getRegionName(regionCode);
- if (regionName != null) {
- return String.format("%1$s (%2$s) in %3$s (%4$s)", languageName, languageCode,
- regionName, regionCode);
- }
- return String.format("%1$s (%2$s) in %3$s", languageName, languageCode,
- regionCode);
- }
- return String.format("%1$s / %2$s", languageCode, regionCode);
- }
- }
-
- // ---- Implements DevicesChangedListener ----
-
- @Override
- public void onDevicesChanged() {
- final Sdk sdk = Sdk.getCurrent();
- if (sdk != null) {
- mDevices = sdk.getDeviceManager().getDevices(DeviceManager.ALL_DEVICES);
- } else {
- mDevices = new ArrayList<Device>();
- }
- }
-
- // ---- Reacting to UI changes ----
-
- /**
- * Called when the selection of the device combo changes.
- */
- void onDeviceChange() {
- // because changing the content of a combo triggers a change event, respect the
- // mDisableUpdates flag
- if (mDisableUpdates > 0) {
- return;
- }
-
- // Attempt to preserve the device state
- String stateName = null;
- Device prevDevice = mConfiguration.getDevice();
- State prevState = mConfiguration.getDeviceState();
- Device device = (Device) mDeviceCombo.getData();
- if (prevDevice != null && prevState != null && device != null) {
- // get the previous config, so that we can look for a close match
- FolderConfiguration oldConfig = DeviceConfigHelper.getFolderConfig(prevState);
- if (oldConfig != null) {
- stateName = ConfigurationMatcher.getClosestMatch(oldConfig, device.getAllStates());
- }
- }
- mConfiguration.setDevice(device, true);
- State newState = Configuration.getState(device, stateName);
- mConfiguration.setDeviceState(newState, true);
- selectDeviceState(newState);
- mConfiguration.syncFolderConfig();
-
- // Notify
- IFile file = mEditedFile;
- boolean accepted = mClient.changed(CFG_DEVICE | CFG_DEVICE_STATE);
- if (!accepted) {
- mConfiguration.setDevice(prevDevice, true);
- mConfiguration.setDeviceState(prevState, true);
- mConfiguration.syncFolderConfig();
- selectDevice(prevDevice);
- selectDeviceState(prevState);
- return;
- } else {
- syncToVariations(CFG_DEVICE | CFG_DEVICE_STATE, file, mConfiguration, false, true);
- }
-
- saveConstraints();
- }
-
- /**
- * Synchronizes changes to the given attributes (indicated by the mask
- * referencing the {@code CFG_} configuration attribute bit flags in
- * {@link Configuration} to the layout variations of the given updated file.
- *
- * @param flags the attributes which were updated
- * @param updatedFile the file which was updated
- * @param base the base configuration to base the chooser off of
- * @param includeSelf whether the updated file itself should be updated
- * @param async whether the updates should be performed asynchronously
- */
- public void syncToVariations(
- final int flags,
- final @NonNull IFile updatedFile,
- final @NonNull Configuration base,
- final boolean includeSelf,
- boolean async) {
- if (async) {
- getDisplay().asyncExec(new Runnable() {
- @Override
- public void run() {
- doSyncToVariations(flags, updatedFile, includeSelf, base);
- }
- });
- } else {
- doSyncToVariations(flags, updatedFile, includeSelf, base);
- }
- }
-
- private void doSyncToVariations(int flags, IFile updatedFile, boolean includeSelf,
- Configuration base) {
- // Synchronize the given changes to other configurations as well
- List<IFile> files = AdtUtils.getResourceVariations(updatedFile, includeSelf);
- for (IFile file : files) {
- Configuration configuration = Configuration.create(base, file);
- configuration.setTheme(base.getTheme());
- configuration.setActivity(base.getActivity());
- Collection<IEditorPart> editors = AdtUtils.findEditorsFor(file, false);
- boolean found = false;
- for (IEditorPart editor : editors) {
- if (editor instanceof CommonXmlEditor) {
- CommonXmlDelegate delegate = ((CommonXmlEditor) editor).getDelegate();
- if (delegate instanceof LayoutEditorDelegate) {
- editor = ((LayoutEditorDelegate) delegate).getGraphicalEditor();
- }
- }
- if (editor instanceof GraphicalEditorPart) {
- ConfigurationChooser chooser =
- ((GraphicalEditorPart) editor).getConfigurationChooser();
- chooser.setConfiguration(configuration);
- found = true;
- }
- }
- if (!found) {
- // Just update the file persistence
- String description = configuration.toPersistentString();
- ConfigurationDescription.setDescription(file, description);
- }
- }
- }
-
- /**
- * Called when the device config selection changes.
- */
- void onDeviceConfigChange() {
- // because changing the content of a combo triggers a change event, respect the
- // mDisableUpdates flag
- if (mDisableUpdates > 0) {
- return;
- }
-
- State prev = mConfiguration.getDeviceState();
- State state = (State) mOrientationCombo.getData();
- mConfiguration.setDeviceState(state, false);
-
- if (mClient != null) {
- boolean accepted = mClient.changed(CFG_DEVICE | CFG_DEVICE_STATE);
- if (!accepted) {
- mConfiguration.setDeviceState(prev, false);
- selectDeviceState(prev);
- return;
- }
- }
-
- saveConstraints();
- }
-
- /**
- * Call back for language combo selection
- */
- void onLocaleChange() {
- // because mLocaleList triggers onLocaleChange at each modification, the filling
- // of the combo with data will trigger notifications, and we don't want that.
- if (mDisableUpdates > 0) {
- return;
- }
-
- Locale prev = mConfiguration.getLocale();
- Locale locale = (Locale) mLocaleCombo.getData();
- if (locale == null) {
- locale = Locale.ANY;
- }
- mConfiguration.setLocale(locale, false);
-
- if (mClient != null) {
- boolean accepted = mClient.changed(CFG_LOCALE);
- if (!accepted) {
- mConfiguration.setLocale(prev, false);
- selectLocale(prev);
- }
- }
-
- // Store locale project-wide setting
- mConfiguration.saveRenderState();
- }
-
-
- void onThemeChange() {
- if (mDisableUpdates > 0) {
- return;
- }
-
- String prev = mConfiguration.getTheme();
- mConfiguration.setTheme((String) mThemeCombo.getData());
-
- if (mClient != null) {
- boolean accepted = mClient.changed(CFG_THEME);
- if (!accepted) {
- mConfiguration.setTheme(prev);
- selectTheme(prev);
- return;
- } else {
- syncToVariations(CFG_DEVICE|CFG_DEVICE_STATE, mEditedFile, mConfiguration,
- false, true);
- }
- }
-
- saveConstraints();
- }
-
- void notifyFolderConfigChanged() {
- if (mDisableUpdates > 0 || mClient == null) {
- return;
- }
-
- if (mClient.changed(CFG_FOLDER)) {
- saveConstraints();
- }
- }
-
- void onSelectActivity() {
- if (mDisableUpdates > 0) {
- return;
- }
-
- String activity = (String) mActivityCombo.getData();
- mConfiguration.setActivity(activity);
-
- if (activity == null) {
- return;
- }
-
- // See if there is a default theme assigned to this activity, and if so, use it
- ManifestInfo manifest = ManifestInfo.get(mEditedFile.getProject());
- String preferred = null;
- ActivityAttributes attributes = manifest.getActivityAttributes(activity);
- if (attributes != null) {
- preferred = attributes.getTheme();
- }
- if (preferred != null && !Objects.equal(preferred, mConfiguration.getTheme())) {
- // Yes, switch to it
- selectTheme(preferred);
- onThemeChange();
- }
-
- // Persist in XML
- if (mClient != null) {
- mClient.setActivity(activity);
- }
-
- saveConstraints();
- }
-
- /**
- * Call back for api level combo selection
- */
- void onRenderingTargetChange() {
- // because mApiCombo triggers onApiLevelChange at each modification, the filling
- // of the combo with data will trigger notifications, and we don't want that.
- if (mDisableUpdates > 0) {
- return;
- }
-
- IAndroidTarget prevTarget = mConfiguration.getTarget();
- String prevTheme = mConfiguration.getTheme();
-
- int changeFlags = 0;
-
- // tell the listener a new rendering target is being set. Need to do this before updating
- // mRenderingTarget.
- if (prevTarget != null) {
- changeFlags |= CFG_TARGET;
- mClient.aboutToChange(changeFlags);
- }
-
- IAndroidTarget target = (IAndroidTarget) mTargetCombo.getData();
- mConfiguration.setTarget(target, true);
-
- // force a theme update to reflect the new rendering target.
- // This must be done after computeCurrentConfig since it'll depend on the currentConfig
- // to figure out the theme list.
- String oldTheme = mConfiguration.getTheme();
- updateThemes();
- // updateThemes may change the theme (based on theme availability in the new rendering
- // target) so mark theme change if necessary
- if (!Objects.equal(oldTheme, mConfiguration.getTheme())) {
- changeFlags |= CFG_THEME;
- }
-
- if (target != null) {
- changeFlags |= CFG_TARGET;
- changeFlags |= CFG_FOLDER; // In case we added a -vNN qualifier
- }
-
- // Store project-wide render-target setting
- mConfiguration.saveRenderState();
-
- mConfiguration.syncFolderConfig();
-
- if (mClient != null) {
- boolean accepted = mClient.changed(changeFlags);
- if (!accepted) {
- mConfiguration.setTarget(prevTarget, true);
- mConfiguration.setTheme(prevTheme);
- mConfiguration.syncFolderConfig();
- selectTheme(prevTheme);
- selectTarget(prevTarget);
- }
- }
- }
-
- /**
- * Syncs this configuration to the project wide locale and render target settings. The
- * locale may ignore the project-wide setting if it is a locale-specific
- * configuration.
- *
- * @return true if one or both of the toggles were changed, false if there were no
- * changes
- */
- public boolean syncRenderState() {
- if (mConfiguration.getEditedConfig() == null) {
- // Startup; ignore
- return false;
- }
-
- boolean renderTargetChanged = false;
-
- // When a page is re-activated, force the toggles to reflect the current project
- // state
-
- Pair<Locale, IAndroidTarget> pair = Configuration.loadRenderState(this);
-
- int changeFlags = 0;
- // Only sync the locale if this layout is not already a locale-specific layout!
- if (pair != null && !mConfiguration.isLocaleSpecificLayout()) {
- Locale locale = pair.getFirst();
- if (locale != null) {
- boolean localeChanged = setLocale(locale);
- if (localeChanged) {
- changeFlags |= CFG_LOCALE;
- }
- } else {
- locale = Locale.ANY;
- }
- mConfiguration.setLocale(locale, true);
- }
-
- // Sync render target
- IAndroidTarget configurationTarget = mConfiguration.getTarget();
- IAndroidTarget target = pair != null ? pair.getSecond() : configurationTarget;
- if (target != null && configurationTarget != target) {
- if (mClient != null && configurationTarget != null) {
- changeFlags |= CFG_TARGET;
- mClient.aboutToChange(changeFlags);
- }
-
- mConfiguration.setTarget(target, true);
- selectTarget(target);
- renderTargetChanged = true;
- }
-
- // Neither locale nor render target changed: nothing to do
- if (changeFlags == 0) {
- return false;
- }
-
- // Update the locale and/or the render target. This code contains a logical
- // merge of the onRenderingTargetChange() and onLocaleChange() methods, combined
- // such that we don't duplicate work.
-
- // Compute the new configuration; we want to do this both for locale changes
- // and for render targets.
- mConfiguration.syncFolderConfig();
- changeFlags |= CFG_FOLDER; // in case we added/remove a -v<NN> qualifier
-
- if (renderTargetChanged) {
- // force a theme update to reflect the new rendering target.
- // This must be done after computeCurrentConfig since it'll depend on the currentConfig
- // to figure out the theme list.
- updateThemes();
- }
-
- if (mClient != null) {
- mClient.changed(changeFlags);
- }
-
- return true;
- }
-
- // ---- Populate data structures with themes, locales, etc ----
-
- /**
- * Updates the internal list of themes.
- */
- private void updateThemes() {
- if (mClient == null) {
- return; // can't do anything without it.
- }
-
- ResourceRepository frameworkRes = mClient.getFrameworkResources(
- mConfiguration.getTarget());
-
- mDisableUpdates++;
-
- try {
- if (mEditedFile != null) {
- String theme = mConfiguration.getTheme();
- if (theme == null || theme.isEmpty() || mClient.getIncludedWithin() != null) {
- mConfiguration.setTheme(null);
- mConfiguration.computePreferredTheme();
- }
- assert mConfiguration.getTheme() != null;
- }
-
- mThemeList.clear();
-
- ArrayList<String> themes = new ArrayList<String>();
- ResourceRepository projectRes = mClient.getProjectResources();
- // in cases where the opened file is not linked to a project, this could be null.
- if (projectRes != null) {
- // get the configured resources for the project
- Map<ResourceType, Map<String, ResourceValue>> configuredProjectRes =
- mClient.getConfiguredProjectResources();
-
- if (configuredProjectRes != null) {
- // get the styles.
- Map<String, ResourceValue> styleMap = configuredProjectRes.get(
- ResourceType.STYLE);
-
- if (styleMap != null) {
- // collect the themes out of all the styles, ie styles that extend,
- // directly or indirectly a platform theme.
- for (ResourceValue value : styleMap.values()) {
- if (isTheme(value, styleMap, null)) {
- String theme = value.getName();
- themes.add(theme);
- }
- }
-
- Collections.sort(themes);
-
- for (String theme : themes) {
- if (!theme.startsWith(PREFIX_RESOURCE_REF)) {
- theme = STYLE_RESOURCE_PREFIX + theme;
- }
- mThemeList.add(theme);
- }
- }
- }
- themes.clear();
- }
-
- // get the themes, and languages from the Framework.
- if (frameworkRes != null) {
- // get the configured resources for the framework
- Map<ResourceType, Map<String, ResourceValue>> frameworResources =
- frameworkRes.getConfiguredResources(mConfiguration.getFullConfig());
-
- if (frameworResources != null) {
- // get the styles.
- Map<String, ResourceValue> styles = frameworResources.get(ResourceType.STYLE);
-
- // collect the themes out of all the styles.
- for (ResourceValue value : styles.values()) {
- String name = value.getName();
- if (name.startsWith("Theme.") || name.equals("Theme")) { //$NON-NLS-1$ //$NON-NLS-2$
- themes.add(value.getName());
- }
- }
-
- // sort them and add them to the combo
- Collections.sort(themes);
-
- for (String theme : themes) {
- if (!theme.startsWith(PREFIX_RESOURCE_REF)) {
- theme = ANDROID_STYLE_RESOURCE_PREFIX + theme;
- }
- mThemeList.add(theme);
- }
-
- themes.clear();
- }
- }
-
- // Migration: In the past we didn't store the style prefix in the settings;
- // this meant we might lose track of whether the theme is a project style
- // or a framework style. For now we need to migrate. Search through the
- // theme list until we have a match
- String theme = mConfiguration.getTheme();
- if (theme != null && !theme.startsWith(PREFIX_RESOURCE_REF)) {
- String projectStyle = STYLE_RESOURCE_PREFIX + theme;
- String frameworkStyle = ANDROID_STYLE_RESOURCE_PREFIX + theme;
- for (String t : mThemeList) {
- if (t.equals(projectStyle)) {
- mConfiguration.setTheme(projectStyle);
- break;
- } else if (t.equals(frameworkStyle)) {
- mConfiguration.setTheme(frameworkStyle);
- break;
- }
- }
- if (!theme.startsWith(PREFIX_RESOURCE_REF)) {
- // Arbitrary guess
- if (theme.startsWith("Theme.")) {
- theme = ANDROID_STYLE_RESOURCE_PREFIX + theme;
- } else {
- theme = STYLE_RESOURCE_PREFIX + theme;
- }
- }
- }
-
- // TODO: Handle the case where you have a theme persisted that isn't available??
- // We could look up mConfiguration.theme and make sure it appears in the list! And if
- // not, picking one.
- selectTheme(mConfiguration.getTheme());
- } finally {
- mDisableUpdates--;
- }
- }
-
- private void updateActivity() {
- if (mEditedFile != null) {
- String preferred = getPreferredActivity(mEditedFile);
- selectActivity(preferred);
- }
- }
-
- /**
- * Updates the locale combo.
- * This must be called from the UI thread.
- */
- public void updateLocales() {
- if (mClient == null) {
- return; // can't do anything w/o it.
- }
-
- mDisableUpdates++;
-
- try {
- mLocaleList.clear();
-
- SortedSet<String> languages = null;
-
- // get the languages from the project.
- ResourceRepository projectRes = mClient.getProjectResources();
-
- // in cases where the opened file is not linked to a project, this could be null.
- if (projectRes != null) {
- // now get the languages from the project.
- languages = projectRes.getLanguages();
-
- for (String language : languages) {
- // find the matching regions and add them
- SortedSet<String> regions = projectRes.getRegions(language);
- for (String region : regions) {
- LocaleQualifier locale = LocaleQualifier.getQualifier(language + "-r" + region);
- if (locale != null) {
- mLocaleList.add(Locale.create(locale));
- }
- }
-
- // now the entry for the other regions the language alone
- // create a region qualifier that will never be matched by qualified resources.
- LocaleQualifier locale = new LocaleQualifier(language);
- mLocaleList.add(Locale.create(locale));
- }
- }
-
- // create language/region qualifier that will never be matched by qualified resources.
- mLocaleList.add(Locale.ANY);
-
- Locale locale = mConfiguration.getLocale();
- setLocale(locale);
- } finally {
- mDisableUpdates--;
- }
- }
-
- @Nullable
- private String getPreferredActivity(@NonNull IFile file) {
- // Store/restore the activity context in the config state to help with
- // performance if for some reason we can't write it into the XML file and to
- // avoid having to open the model below
- if (mConfiguration.getActivity() != null) {
- return mConfiguration.getActivity();
- }
-
- IProject project = file.getProject();
-
- // Look up from XML file
- Document document = DomUtilities.getDocument(file);
- if (document != null) {
- Element element = document.getDocumentElement();
- if (element != null) {
- String activity = element.getAttributeNS(TOOLS_URI, ATTR_CONTEXT);
- if (activity != null && !activity.isEmpty()) {
- if (activity.startsWith(".") || activity.indexOf('.') == -1) { //$NON-NLS-1$
- ManifestInfo manifest = ManifestInfo.get(project);
- String pkg = manifest.getPackage();
- if (!pkg.isEmpty()) {
- if (activity.startsWith(".")) { //$NON-NLS-1$
- activity = pkg + activity;
- } else {
- activity = activity + '.' + pkg;
- }
- }
- }
-
- mConfiguration.setActivity(activity);
- saveConstraints();
- return activity;
- }
- }
- }
-
- // No, not available there: try to infer it from the code index
- String includedIn = null;
- Reference includedWithin = mClient.getIncludedWithin();
- if (mClient != null && includedWithin != null) {
- includedIn = includedWithin.getName();
- }
-
- ManifestInfo manifest = ManifestInfo.get(project);
- String pkg = manifest.getPackage();
- String layoutName = ResourceHelper.getLayoutName(mEditedFile);
-
- // If we are rendering a layout in included context, pick the theme
- // from the outer layout instead
- if (includedIn != null) {
- layoutName = includedIn;
- }
-
- String activity = ManifestInfo.guessActivity(project, layoutName, pkg);
-
- if (activity == null) {
- List<String> activities = ManifestInfo.getProjectActivities(project);
- if (activities.size() == 1) {
- activity = activities.get(0);
- }
- }
-
- if (activity != null) {
- mConfiguration.setActivity(activity);
- saveConstraints();
- return activity;
- }
-
- // TODO: Do anything else, such as pick the first activity found?
- // Or just leave some default label instead?
- // Also, figure out what to store in the mState so I don't keep trying
-
- return null;
- }
-
- /**
- * Returns whether the given <var>style</var> is a theme.
- * This is done by making sure the parent is a theme.
- * @param value the style to check
- * @param styleMap the map of styles for the current project. Key is the style name.
- * @param seen the map of styles we have already processed (or null if not yet
- * initialized). Only the keys are significant (since there is no IdentityHashSet).
- * @return True if the given <var>style</var> is a theme.
- */
- private static boolean isTheme(ResourceValue value, Map<String, ResourceValue> styleMap,
- IdentityHashMap<ResourceValue, Boolean> seen) {
- if (value instanceof StyleResourceValue) {
- StyleResourceValue style = (StyleResourceValue)value;
-
- boolean frameworkStyle = false;
- String parentStyle = style.getParentStyle();
- if (parentStyle == null) {
- // if there is no specified parent style we look an implied one.
- // For instance 'Theme.light' is implied child style of 'Theme',
- // and 'Theme.light.fullscreen' is implied child style of 'Theme.light'
- String name = style.getName();
- int index = name.lastIndexOf('.');
- if (index != -1) {
- parentStyle = name.substring(0, index);
- }
- } else {
- // remove the useless @ if it's there
- if (parentStyle.startsWith("@")) {
- parentStyle = parentStyle.substring(1);
- }
-
- // check for framework identifier.
- if (parentStyle.startsWith(ANDROID_NS_NAME_PREFIX)) {
- frameworkStyle = true;
- parentStyle = parentStyle.substring(ANDROID_NS_NAME_PREFIX.length());
- }
-
- // at this point we could have the format style/<name>. we want only the name
- if (parentStyle.startsWith("style/")) {
- parentStyle = parentStyle.substring("style/".length());
- }
- }
-
- if (parentStyle != null) {
- if (frameworkStyle) {
- // if the parent is a framework style, it has to be 'Theme' or 'Theme.*'
- return parentStyle.equals("Theme") || parentStyle.startsWith("Theme.");
- } else {
- // if it's a project style, we check this is a theme.
- ResourceValue parentValue = styleMap.get(parentStyle);
-
- // also prevent stack overflow in case the dev mistakenly declared
- // the parent of the style as the style itself.
- if (parentValue != null && !parentValue.equals(value)) {
- if (seen == null) {
- seen = new IdentityHashMap<ResourceValue, Boolean>();
- seen.put(value, Boolean.TRUE);
- } else if (seen.containsKey(parentValue)) {
- return false;
- }
- seen.put(parentValue, Boolean.TRUE);
- return isTheme(parentValue, styleMap, seen);
- }
- }
- }
- }
-
- return false;
- }
-
- /**
- * Returns true if this configuration chooser represents the best match for
- * the given file
- *
- * @param file the file to test
- * @param config the config to test
- * @return true if the given config is the best match for the given file
- */
- public boolean isBestMatchFor(IFile file, FolderConfiguration config) {
- ResourceFile match = mResources.getMatchingFile(mEditedFile.getName(),
- ResourceType.LAYOUT, config);
- if (match != null) {
- return match.getFile().equals(mEditedFile);
- }
-
- return false;
- }
-}