diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationDescription.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationDescription.java | 395 |
1 files changed, 395 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationDescription.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationDescription.java new file mode 100644 index 000000000..956ac1839 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationDescription.java @@ -0,0 +1,395 @@ +/* + * 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_STYLE_RESOURCE_PREFIX; +import static com.android.SdkConstants.ATTR_NAME; +import static com.android.SdkConstants.ATTR_THEME; +import static com.android.SdkConstants.PREFIX_RESOURCE_REF; +import static com.android.SdkConstants.STYLE_RESOURCE_PREFIX; + +import com.android.annotations.NonNull; +import com.android.annotations.Nullable; +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.ScreenSizeQualifier; +import com.android.ide.eclipse.adt.AdtPlugin; +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.sdk.AndroidTargetData; +import com.android.ide.eclipse.adt.internal.sdk.Sdk; +import com.android.resources.NightMode; +import com.android.resources.ResourceFolderType; +import com.android.resources.ScreenSize; +import com.android.resources.UiMode; +import com.android.sdklib.IAndroidTarget; +import com.android.sdklib.devices.Device; +import com.android.sdklib.devices.State; +import com.google.common.base.Splitter; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.QualifiedName; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import java.util.Collection; +import java.util.List; + +/** A description of a configuration, used for persistence */ +public class ConfigurationDescription { + private static final String TAG_PREVIEWS = "previews"; //$NON-NLS-1$ + private static final String TAG_PREVIEW = "preview"; //$NON-NLS-1$ + private static final String ATTR_TARGET = "target"; //$NON-NLS-1$ + private static final String ATTR_CONFIG = "config"; //$NON-NLS-1$ + private static final String ATTR_LOCALE = "locale"; //$NON-NLS-1$ + private static final String ATTR_ACTIVITY = "activity"; //$NON-NLS-1$ + private static final String ATTR_DEVICE = "device"; //$NON-NLS-1$ + private static final String ATTR_STATE = "devicestate"; //$NON-NLS-1$ + private static final String ATTR_UIMODE = "ui"; //$NON-NLS-1$ + private static final String ATTR_NIGHTMODE = "night"; //$NON-NLS-1$ + private final static String SEP_LOCALE = "-"; //$NON-NLS-1$ + + /** + * Settings name for file-specific configuration preferences, such as which theme or + * device to render the current layout with + */ + public final static QualifiedName NAME_CONFIG_STATE = + new QualifiedName(AdtPlugin.PLUGIN_ID, "state");//$NON-NLS-1$ + + /** The project corresponding to this configuration's description */ + public final IProject project; + + /** The display name */ + public String displayName; + + /** The theme */ + public String theme; + + /** The target */ + public IAndroidTarget target; + + /** The display name */ + public FolderConfiguration folder; + + /** The locale */ + public Locale locale = Locale.ANY; + + /** The device */ + public Device device; + + /** The device state */ + public State state; + + /** The activity */ + public String activity; + + /** UI mode */ + @NonNull + public UiMode uiMode = UiMode.NORMAL; + + /** Night mode */ + @NonNull + public NightMode nightMode = NightMode.NOTNIGHT; + + private ConfigurationDescription(@Nullable IProject project) { + this.project = project; + } + + /** + * Returns the persistent configuration description from the given file + * + * @param file the file to look up a description from + * @return the description or null if never written + */ + @Nullable + public static String getDescription(@NonNull IFile file) { + return AdtPlugin.getFileProperty(file, NAME_CONFIG_STATE); + } + + /** + * Sets the persistent configuration description data for the given file + * + * @param file the file to associate the description with + * @param description the description + */ + public static void setDescription(@NonNull IFile file, @NonNull String description) { + AdtPlugin.setFileProperty(file, NAME_CONFIG_STATE, description); + } + + /** + * Creates a description from a given configuration + * + * @param project the project for this configuration's description + * @param configuration the configuration to describe + * @return a new configuration + */ + public static ConfigurationDescription fromConfiguration( + @Nullable IProject project, + @NonNull Configuration configuration) { + ConfigurationDescription description = new ConfigurationDescription(project); + description.displayName = configuration.getDisplayName(); + description.theme = configuration.getTheme(); + description.target = configuration.getTarget(); + description.folder = new FolderConfiguration(); + description.folder.set(configuration.getFullConfig()); + description.locale = configuration.getLocale(); + description.device = configuration.getDevice(); + description.state = configuration.getDeviceState(); + description.activity = configuration.getActivity(); + return description; + } + + /** + * Initializes a string previously created with + * {@link #toXml(Document)} + * + * @param project the project for this configuration's description + * @param element the element to read back from + * @param deviceList list of available devices + * @return true if the configuration was initialized + */ + @Nullable + public static ConfigurationDescription fromXml( + @Nullable IProject project, + @NonNull Element element, + @NonNull Collection<Device> deviceList) { + ConfigurationDescription description = new ConfigurationDescription(project); + + if (!TAG_PREVIEW.equals(element.getTagName())) { + return null; + } + + String displayName = element.getAttribute(ATTR_NAME); + if (!displayName.isEmpty()) { + description.displayName = displayName; + } + + String config = element.getAttribute(ATTR_CONFIG); + Iterable<String> segments = Splitter.on('-').split(config); + description.folder = FolderConfiguration.getConfig(segments); + + String theme = element.getAttribute(ATTR_THEME); + if (!theme.isEmpty()) { + description.theme = theme; + } + + String targetId = element.getAttribute(ATTR_TARGET); + if (!targetId.isEmpty()) { + IAndroidTarget target = Configuration.stringToTarget(targetId); + description.target = target; + } + + String localeString = element.getAttribute(ATTR_LOCALE); + if (!localeString.isEmpty()) { + // Load locale. Note that this can get overwritten by the + // project-wide settings read below. + String locales[] = localeString.split(SEP_LOCALE); + if (locales[0].length() > 0 && !LocaleQualifier.FAKE_VALUE.equals(locales[0])) { + String language = locales[0]; + if (locales.length >= 2 && locales[1].length() > 0 && !LocaleQualifier.FAKE_VALUE.equals(locales[1])) { + description.locale = Locale.create(LocaleQualifier.getQualifier(language + "-r" + locales[1])); + } else { + description.locale = Locale.create(new LocaleQualifier(language)); + } + } else { + description.locale = Locale.ANY; + } + + + } + + String activity = element.getAttribute(ATTR_ACTIVITY); + if (activity.isEmpty()) { + activity = null; + } + + String deviceString = element.getAttribute(ATTR_DEVICE); + if (!deviceString.isEmpty()) { + for (Device d : deviceList) { + if (d.getName().equals(deviceString)) { + description.device = d; + String stateName = element.getAttribute(ATTR_STATE); + if (stateName.isEmpty() || stateName.equals("null")) { + description.state = Configuration.getState(d, stateName); + } else if (d.getAllStates().size() > 0) { + description.state = d.getAllStates().get(0); + } + break; + } + } + } + + String uiModeString = element.getAttribute(ATTR_UIMODE); + if (!uiModeString.isEmpty()) { + description.uiMode = UiMode.getEnum(uiModeString); + if (description.uiMode == null) { + description.uiMode = UiMode.NORMAL; + } + } + + String nightModeString = element.getAttribute(ATTR_NIGHTMODE); + if (!nightModeString.isEmpty()) { + description.nightMode = NightMode.getEnum(nightModeString); + if (description.nightMode == null) { + description.nightMode = NightMode.NOTNIGHT; + } + } + + + // Should I really be storing the FULL configuration? Might be trouble if + // you bring a different device + + return description; + } + + /** + * Write this description into the given document as a new element. + * + * @param document the document to add the description to + * @return the newly inserted element + */ + @NonNull + public Element toXml(Document document) { + Element element = document.createElement(TAG_PREVIEW); + + element.setAttribute(ATTR_NAME, displayName); + FolderConfiguration fullConfig = folder; + String folderName = fullConfig.getFolderName(ResourceFolderType.LAYOUT); + element.setAttribute(ATTR_CONFIG, folderName); + if (theme != null) { + element.setAttribute(ATTR_THEME, theme); + } + if (target != null) { + element.setAttribute(ATTR_TARGET, Configuration.targetToString(target)); + } + + if (locale != null && (locale.hasLanguage() || locale.hasRegion())) { + String value; + if (locale.hasRegion()) { + value = locale.qualifier.getLanguage() + SEP_LOCALE + locale.qualifier.getRegion(); + } else { + value = locale.qualifier.getLanguage(); + } + element.setAttribute(ATTR_LOCALE, value); + } + + if (device != null) { + element.setAttribute(ATTR_DEVICE, device.getName()); + if (state != null) { + element.setAttribute(ATTR_STATE, state.getName()); + } + } + + if (activity != null) { + element.setAttribute(ATTR_ACTIVITY, activity); + } + + if (uiMode != null && uiMode != UiMode.NORMAL) { + element.setAttribute(ATTR_UIMODE, uiMode.getResourceValue()); + } + + if (nightMode != null && nightMode != NightMode.NOTNIGHT) { + element.setAttribute(ATTR_NIGHTMODE, nightMode.getResourceValue()); + } + + Element parent = document.getDocumentElement(); + if (parent == null) { + parent = document.createElement(TAG_PREVIEWS); + document.appendChild(parent); + } + parent.appendChild(element); + + return element; + } + + /** Returns the preferred theme, or null */ + @Nullable + String computePreferredTheme() { + if (project == null) { + return "Theme"; + } + ManifestInfo manifest = ManifestInfo.get(project); + + // Look up the screen size for the current state + ScreenSize screenSize = null; + if (device != null) { + List<State> states = device.getAllStates(); + for (State s : states) { + FolderConfiguration folderConfig = DeviceConfigHelper.getFolderConfig(s); + if (folderConfig != null) { + ScreenSizeQualifier qualifier = folderConfig.getScreenSizeQualifier(); + screenSize = qualifier.getValue(); + break; + } + } + } + + // Look up the default/fallback theme to use for this project (which + // depends on the screen size when no particular theme is specified + // in the manifest) + String defaultTheme = manifest.getDefaultTheme(target, screenSize); + + String preferred = defaultTheme; + if (theme == null) { + // If we are rendering a layout in included context, pick the theme + // from the outer layout instead + + if (activity != null) { + ActivityAttributes attributes = manifest.getActivityAttributes(activity); + if (attributes != null) { + preferred = attributes.getTheme(); + } + } + if (preferred == null) { + preferred = defaultTheme; + } + theme = preferred; + } + + return preferred; + } + + private void checkThemePrefix() { + if (theme != null && !theme.startsWith(PREFIX_RESOURCE_REF)) { + if (theme.isEmpty()) { + computePreferredTheme(); + return; + } + + if (target != null) { + Sdk sdk = Sdk.getCurrent(); + if (sdk != null) { + AndroidTargetData data = sdk.getTargetData(target); + + if (data != null) { + ResourceRepository resources = data.getFrameworkResources(); + if (resources != null + && resources.hasResourceItem(ANDROID_STYLE_RESOURCE_PREFIX + theme)) { + theme = ANDROID_STYLE_RESOURCE_PREFIX + theme; + return; + } + } + } + } + + theme = STYLE_RESOURCE_PREFIX + theme; + } + } +} |