| /* |
| * Copyright (C) 2008 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 com.android.ide.eclipse.adt.internal.editors.IconFactory; |
| import com.android.ide.eclipse.adt.internal.resources.ResourceType; |
| import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration; |
| import com.android.ide.eclipse.adt.internal.resources.configurations.LanguageQualifier; |
| import com.android.ide.eclipse.adt.internal.resources.configurations.PixelDensityQualifier; |
| import com.android.ide.eclipse.adt.internal.resources.configurations.RegionQualifier; |
| import com.android.ide.eclipse.adt.internal.resources.configurations.ResourceQualifier; |
| import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenDimensionQualifier; |
| import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenOrientationQualifier; |
| import com.android.ide.eclipse.adt.internal.resources.configurations.VersionQualifier; |
| import com.android.ide.eclipse.adt.internal.resources.configurations.PixelDensityQualifier.Density; |
| import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenOrientationQualifier.ScreenOrientation; |
| import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources; |
| import com.android.ide.eclipse.adt.internal.sdk.LayoutDevice; |
| import com.android.ide.eclipse.adt.internal.sdk.LayoutDeviceManager; |
| import com.android.ide.eclipse.adt.internal.sdk.Sdk; |
| import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.LanguageRegionVerifier; |
| import com.android.layoutlib.api.IResourceValue; |
| import com.android.layoutlib.api.IStyleResourceValue; |
| |
| import org.eclipse.draw2d.geometry.Rectangle; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.SelectionAdapter; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.events.SelectionListener; |
| import org.eclipse.swt.layout.GridData; |
| import org.eclipse.swt.layout.GridLayout; |
| import org.eclipse.swt.widgets.Button; |
| import org.eclipse.swt.widgets.Combo; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Label; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.SortedSet; |
| |
| /** |
| * A composite that displays the current configuration displayed in a Graphical Layout Editor. |
| */ |
| public class ConfigurationComposite extends Composite { |
| |
| private final static String THEME_SEPARATOR = "----------"; //$NON-NLS-1$ |
| |
| private Button mClippingButton; |
| private Label mCurrentLayoutLabel; |
| |
| private Combo mLocale; |
| private Combo mDeviceList; |
| private Combo mDeviceConfigs; |
| private Combo mThemeCombo; |
| private Button mCreateButton; |
| |
| |
| private int mPlatformThemeCount = 0; |
| private boolean mDisableUpdates = false; |
| |
| /** The {@link FolderConfiguration} representing the state of the UI controls */ |
| private final FolderConfiguration mCurrentConfig = new FolderConfiguration(); |
| |
| private List<LayoutDevice> mDevices; |
| |
| private final ArrayList<ResourceQualifier[] > mLocaleList = |
| new ArrayList<ResourceQualifier[]>(); |
| |
| private final IConfigListener mListener; |
| |
| private boolean mClipping = true; |
| |
| private LayoutDevice mCurrentDevice; |
| |
| /** |
| * Interface implemented by the part which owns a {@link ConfigurationComposite}. |
| * This notifies the owners when the configuration change. |
| * The owner must also provide methods to provide the configuration that will |
| * be displayed. |
| */ |
| public interface IConfigListener { |
| void onConfigurationChange(); |
| void onThemeChange(); |
| void onCreate(); |
| void OnClippingChange(); |
| |
| ProjectResources getProjectResources(); |
| ProjectResources getFrameworkResources(); |
| Map<String, Map<String, IResourceValue>> getConfiguredProjectResources(); |
| Map<String, Map<String, IResourceValue>> getConfiguredFrameworkResources(); |
| } |
| |
| public ConfigurationComposite(IConfigListener listener, Composite parent, int style) { |
| super(parent, style); |
| mListener = listener; |
| |
| GridLayout gl; |
| GridData gd; |
| int cols = 10; // device*2+config*2+locale*2+separator*2+theme+createBtn |
| |
| // ---- First line: collapse button, clipping button, editing config display. |
| Composite labelParent = new Composite(this, SWT.NONE); |
| labelParent.setLayout(gl = new GridLayout(3, false)); |
| gl.marginWidth = gl.marginHeight = 0; |
| labelParent.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); |
| gd.horizontalSpan = cols; |
| |
| new Label(labelParent, SWT.NONE).setText("Editing config:"); |
| mCurrentLayoutLabel = new Label(labelParent, SWT.NONE); |
| mCurrentLayoutLabel.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); |
| gd.widthHint = 50; |
| |
| mClippingButton = new Button(labelParent, SWT.TOGGLE | SWT.FLAT); |
| mClippingButton.setSelection(mClipping); |
| mClippingButton.setToolTipText("Toggles screen clipping on/off"); |
| mClippingButton.setImage(IconFactory.getInstance().getIcon("clipping")); //$NON-NLS-1$ |
| mClippingButton.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| OnClippingChange(); |
| } |
| }); |
| |
| // ---- 2nd line: device/config/locale/theme Combos, create button. |
| |
| setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); |
| setLayout(gl = new GridLayout(cols, false)); |
| gl.marginHeight = 0; |
| gl.horizontalSpacing = 0; |
| |
| new Label(this, SWT.NONE).setText("Devices"); |
| mDeviceList = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY); |
| mDeviceList.setLayoutData(new GridData( |
| GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL)); |
| mDeviceList.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| onDeviceChange(true /* recomputeLayout*/); |
| } |
| }); |
| |
| new Label(this, SWT.NONE).setText("Config"); |
| mDeviceConfigs = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY); |
| mDeviceConfigs.setLayoutData(new GridData( |
| GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL)); |
| mDeviceConfigs.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| onDeviceConfigChange(); |
| } |
| }); |
| |
| new Label(this, SWT.NONE).setText("Locale"); |
| mLocale = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY); |
| mLocale.setLayoutData(new GridData( |
| GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL)); |
| mLocale.addVerifyListener(new LanguageRegionVerifier()); |
| mLocale.addSelectionListener(new SelectionListener() { |
| public void widgetDefaultSelected(SelectionEvent e) { |
| onLocaleChange(); |
| } |
| public void widgetSelected(SelectionEvent e) { |
| onLocaleChange(); |
| } |
| }); |
| |
| // first separator |
| Label separator = new Label(this, SWT.SEPARATOR | SWT.VERTICAL); |
| separator.setLayoutData(gd = new GridData( |
| GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_VERTICAL)); |
| gd.heightHint = 0; |
| |
| mThemeCombo = new Combo(this, SWT.READ_ONLY | SWT.DROP_DOWN); |
| mThemeCombo.setEnabled(false); |
| updateUIFromResources(); |
| mThemeCombo.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| onThemeChange(); |
| } |
| }); |
| |
| // second separator |
| separator = new Label(this, SWT.SEPARATOR | SWT.VERTICAL); |
| separator.setLayoutData(gd = new GridData( |
| GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_VERTICAL)); |
| gd.heightHint = 0; |
| |
| mCreateButton = new Button(this, SWT.PUSH | SWT.FLAT); |
| mCreateButton.setText("Create..."); |
| mCreateButton.setEnabled(false); |
| mCreateButton.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| if (mListener != null) { |
| mListener.onCreate(); |
| } |
| } |
| }); |
| |
| initUiWithDevices(); |
| |
| onDeviceConfigChange(); |
| } |
| |
| |
| public FolderConfiguration getCurrentConfig() { |
| return mCurrentConfig; |
| } |
| |
| public void getCurrentConfig(FolderConfiguration config) { |
| config.set(mCurrentConfig); |
| } |
| |
| /** |
| * Returns the currently selected {@link Density}. This is guaranteed to be non null. |
| */ |
| public Density getDensity() { |
| if (mCurrentConfig != null) { |
| PixelDensityQualifier qual = mCurrentConfig.getPixelDensityQualifier(); |
| if (qual != null) { |
| // just a sanity check |
| Density d = qual.getValue(); |
| if (d != Density.NODPI) { |
| return d; |
| } |
| } |
| } |
| |
| // no config? return medium as the default density. |
| return Density.MEDIUM; |
| } |
| |
| /** |
| * Returns the current device xdpi. |
| */ |
| public float getXDpi() { |
| if (mCurrentDevice != null) { |
| float dpi = mCurrentDevice.getXDpi(); |
| if (Float.isNaN(dpi) == false) { |
| return dpi; |
| } |
| } |
| |
| // get the pixel density as the density. |
| return getDensity().getDpiValue(); |
| } |
| |
| /** |
| * Returns the current device ydpi. |
| */ |
| public float getYDpi() { |
| if (mCurrentDevice != null) { |
| float dpi = mCurrentDevice.getYDpi(); |
| if (Float.isNaN(dpi) == false) { |
| return dpi; |
| } |
| } |
| |
| // get the pixel density as the density. |
| return getDensity().getDpiValue(); |
| } |
| |
| public Rectangle getScreenBounds() { |
| // get the orientation from the current device config |
| ScreenOrientationQualifier qual = mCurrentConfig.getScreenOrientationQualifier(); |
| ScreenOrientation orientation = ScreenOrientation.PORTRAIT; |
| if (qual != null) { |
| orientation = qual.getValue(); |
| } |
| |
| // get the device screen dimension |
| ScreenDimensionQualifier qual2 = mCurrentConfig.getScreenDimensionQualifier(); |
| int s1, s2; |
| if (qual2 != null) { |
| s1 = qual2.getValue1(); |
| s2 = qual2.getValue2(); |
| } else { |
| s1 = 480; |
| s2 = 320; |
| } |
| |
| switch (orientation) { |
| default: |
| case PORTRAIT: |
| return new Rectangle(0, 0, s2, s1); |
| case LANDSCAPE: |
| return new Rectangle(0, 0, s1, s2); |
| case SQUARE: |
| return new Rectangle(0, 0, s1, s1); |
| } |
| } |
| |
| /** |
| * Updates the UI from values in the resources, such as languages, regions, themes, etc... |
| * This must be called from the UI thread. |
| */ |
| public void updateUIFromResources() { |
| if (mListener == null) { |
| return; // can't do anything w/o it. |
| } |
| |
| ProjectResources frameworkProject = mListener.getFrameworkResources(); |
| |
| mDisableUpdates = true; |
| |
| // Reset stuff |
| int selection = mThemeCombo.getSelectionIndex(); |
| mThemeCombo.removeAll(); |
| mPlatformThemeCount = 0; |
| |
| mLocale.removeAll(); |
| mLocaleList.clear(); |
| |
| SortedSet<String> languages = null; |
| ArrayList<String> themes = new ArrayList<String>(); |
| |
| // get the themes, and languages from the Framework. |
| if (frameworkProject != null) { |
| // get the configured resources for the framework |
| Map<String, Map<String, IResourceValue>> frameworResources = |
| mListener.getConfiguredFrameworkResources(); |
| |
| if (frameworResources != null) { |
| // get the styles. |
| Map<String, IResourceValue> styles = frameworResources.get( |
| ResourceType.STYLE.getName()); |
| |
| |
| // collect the themes out of all the styles. |
| for (IResourceValue value : styles.values()) { |
| String name = value.getName(); |
| if (name.startsWith("Theme.") || name.equals("Theme")) { |
| themes.add(value.getName()); |
| mPlatformThemeCount++; |
| } |
| } |
| |
| // sort them and add them to the combo |
| Collections.sort(themes); |
| |
| for (String theme : themes) { |
| mThemeCombo.add(theme); |
| } |
| |
| mPlatformThemeCount = themes.size(); |
| themes.clear(); |
| } |
| } |
| |
| // now get the themes and languages from the project. |
| ProjectResources project = mListener.getProjectResources(); |
| // in cases where the opened file is not linked to a project, this could be null. |
| if (project != null) { |
| // get the configured resources for the project |
| Map<String, Map<String, IResourceValue>> configuredProjectRes = |
| mListener.getConfiguredProjectResources(); |
| |
| if (configuredProjectRes != null) { |
| // get the styles. |
| Map<String, IResourceValue> styleMap = configuredProjectRes.get( |
| ResourceType.STYLE.getName()); |
| |
| if (styleMap != null) { |
| // collect the themes out of all the styles, ie styles that extend, |
| // directly or indirectly a platform theme. |
| for (IResourceValue value : styleMap.values()) { |
| if (isTheme(value, styleMap)) { |
| themes.add(value.getName()); |
| } |
| } |
| |
| // sort them and add them the to the combo. |
| if (mPlatformThemeCount > 0 && themes.size() > 0) { |
| mThemeCombo.add(THEME_SEPARATOR); |
| } |
| |
| Collections.sort(themes); |
| |
| for (String theme : themes) { |
| mThemeCombo.add(theme); |
| } |
| } |
| } |
| |
| // now get the languages from the project. |
| languages = project.getLanguages(); |
| } |
| |
| // add the languages to the Combo |
| mLocale.add("Default"); |
| mLocaleList.add(new ResourceQualifier[] { null, null }); |
| |
| if (project != null && languages != null && languages.size() > 0) { |
| for (String language : languages) { |
| // first the language alone |
| mLocale.add(language); |
| LanguageQualifier qual = new LanguageQualifier(language); |
| mLocaleList.add(new ResourceQualifier[] { qual, null }); |
| |
| // now find the matching regions and add them |
| SortedSet<String> regions = project.getRegions(language); |
| for (String region : regions) { |
| mLocale.add(String.format("%1$s_%2$s", language, region)); //$NON-NLS-1$ |
| RegionQualifier qual2 = new RegionQualifier(region); |
| mLocaleList.add(new ResourceQualifier[] { qual, qual2 }); |
| } |
| |
| } |
| } |
| |
| mDisableUpdates = false; |
| |
| // handle default selection of themes |
| if (mThemeCombo.getItemCount() > 0) { |
| mThemeCombo.setEnabled(true); |
| if (selection == -1) { |
| selection = 0; |
| } |
| |
| if (mThemeCombo.getItemCount() <= selection) { |
| mThemeCombo.select(0); |
| } else { |
| mThemeCombo.select(selection); |
| } |
| } else { |
| mThemeCombo.setEnabled(false); |
| } |
| |
| mThemeCombo.getParent().layout(); |
| } |
| |
| /** |
| * Returns the current theme, or null if the combo has no selection. |
| */ |
| public String getTheme() { |
| int themeIndex = mThemeCombo.getSelectionIndex(); |
| if (themeIndex != -1) { |
| return mThemeCombo.getItem(themeIndex); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Returns whether the current theme selection is a project theme. |
| * <p/>The returned value is meaningless if {@link #getTheme()} returns <code>null</code>. |
| * @return true for project theme, false for framework theme |
| */ |
| public boolean isProjectTheme() { |
| return mThemeCombo.getSelectionIndex() >= mPlatformThemeCount; |
| } |
| |
| public boolean getClipping() { |
| return mClipping; |
| } |
| |
| public void setEnabledCreate(boolean enabled) { |
| mCreateButton.setEnabled(enabled); |
| } |
| |
| public void setClippingSupport(boolean b) { |
| mClippingButton.setEnabled(b); |
| if (b) { |
| mClippingButton.setToolTipText("Toggles screen clipping on/off"); |
| } else { |
| mClipping = true; |
| mClippingButton.setSelection(true); |
| mClippingButton.setToolTipText("Non clipped rendering is not supported"); |
| } |
| } |
| |
| /** |
| * Update the UI controls state with a given {@link FolderConfiguration}. |
| * <p/>If <var>force</var> is set to <code>true</code> the UI will be changed to exactly reflect |
| * <var>config</var>, otherwise, if a qualifier is not present in <var>config</var>, |
| * the UI control is not modified. However if the value in the control is not the default value, |
| * a warning icon is shown. |
| * @param config The {@link FolderConfiguration} to set. |
| * @param force Whether the UI should be changed to exactly match the received configuration. |
| */ |
| public void setConfiguration(FolderConfiguration config, boolean force) { |
| mDisableUpdates = true; // we do not want to trigger onXXXChange when setting new values in the widgets. |
| |
| // TODO: find a device that can display this particular config or create a custom one if needed. |
| |
| |
| // update the string showing the folder name |
| String current = config.toDisplayString(); |
| mCurrentLayoutLabel.setText(current != null ? current : "(Default)"); |
| |
| mDisableUpdates = false; |
| } |
| |
| /** |
| * Reloads the list of {@link LayoutDevice} from the {@link Sdk}. |
| * @param notifyListener |
| */ |
| public void reloadDevices(boolean notifyListener) { |
| loadDevices(); |
| initUiWithDevices(); |
| onDeviceChange(notifyListener); |
| } |
| |
| private void loadDevices() { |
| mDevices = null; |
| |
| Sdk sdk = Sdk.getCurrent(); |
| if (sdk != null) { |
| LayoutDeviceManager manager = sdk.getLayoutDeviceManager(); |
| mDevices = manager.getCombinedList(); |
| } |
| } |
| |
| /** |
| * Init the UI with the list of Devices. |
| */ |
| private void initUiWithDevices() { |
| // remove older devices if applicable |
| mDeviceList.removeAll(); |
| mDeviceConfigs.removeAll(); |
| |
| // fill with the devices |
| if (mDevices != null) { |
| for (LayoutDevice device : mDevices) { |
| mDeviceList.add(device.getName()); |
| } |
| mDeviceList.select(0); |
| |
| if (mDevices.size() > 0) { |
| Map<String, FolderConfiguration> configs = mDevices.get(0).getConfigs(); |
| Set<String> configNames = configs.keySet(); |
| for (String name : configNames) { |
| mDeviceConfigs.add(name); |
| } |
| mDeviceConfigs.select(0); |
| if (configNames.size() == 1) { |
| mDeviceConfigs.setEnabled(false); |
| } |
| } |
| } |
| |
| // add the custom item |
| mDeviceList.add("Custom..."); |
| } |
| |
| /** |
| * Call back for language combo selection |
| */ |
| private void onLocaleChange() { |
| // because mLanguage triggers onLanguageChange at each modification, the filling |
| // of the combo with data will trigger notifications, and we don't want that. |
| if (mDisableUpdates == true) { |
| return; |
| } |
| |
| int localeIndex = mLocale.getSelectionIndex(); |
| ResourceQualifier[] localeQualifiers = mLocaleList.get(localeIndex); |
| |
| mCurrentConfig.setLanguageQualifier((LanguageQualifier)localeQualifiers[0]); // language |
| mCurrentConfig.setRegionQualifier((RegionQualifier)localeQualifiers[1]); // region |
| |
| if (mListener != null) { |
| mListener.onConfigurationChange(); |
| } |
| } |
| |
| private void onDeviceChange(boolean recomputeLayout) { |
| |
| int deviceIndex = mDeviceList.getSelectionIndex(); |
| if (deviceIndex != -1) { |
| // check if the user is ask for the custom item |
| if (deviceIndex == mDeviceList.getItemCount() - 1) { |
| ConfigManagerDialog dialog = new ConfigManagerDialog(getShell()); |
| dialog.open(); |
| |
| // save the user devices |
| Sdk.getCurrent().getLayoutDeviceManager().save(); |
| |
| // reload the combo with the new content. |
| loadDevices(); |
| initUiWithDevices(); |
| |
| // at this point we need to reset the combo to something (hopefully) valid. |
| // look for the previous selected device |
| int index = mDevices.indexOf(mCurrentDevice); |
| if (index != -1) { |
| mDeviceList.select(index); |
| } else { |
| // we should at least have one built-in device, so we select it |
| mDeviceList.select(0); |
| } |
| |
| // force a redraw |
| onDeviceChange(true /*recomputeLayout*/); |
| |
| return; |
| } |
| |
| mCurrentDevice = mDevices.get(deviceIndex); |
| } else { |
| mCurrentDevice = null; |
| } |
| |
| mDeviceConfigs.removeAll(); |
| |
| if (mCurrentDevice != null) { |
| Set<String> configNames = mCurrentDevice.getConfigs().keySet(); |
| for (String name : configNames) { |
| mDeviceConfigs.add(name); |
| } |
| |
| mDeviceConfigs.select(0); |
| if (configNames.size() == 1) { |
| mDeviceConfigs.setEnabled(false); |
| } |
| |
| } |
| if (recomputeLayout) { |
| onDeviceConfigChange(); |
| } |
| } |
| |
| private void onDeviceConfigChange() { |
| if (mCurrentDevice != null) { |
| int configIndex = mDeviceConfigs.getSelectionIndex(); |
| String name = mDeviceConfigs.getItem(configIndex); |
| FolderConfiguration config = mCurrentDevice.getConfigs().get(name); |
| |
| // get the current qualifiers from the current config |
| LanguageQualifier lang = mCurrentConfig.getLanguageQualifier(); |
| RegionQualifier region = mCurrentConfig.getRegionQualifier(); |
| VersionQualifier version = mCurrentConfig.getVersionQualifier(); |
| |
| // replace the config with the one from the device |
| mCurrentConfig.set(config); |
| |
| // and put back the rest of the qualifiers |
| mCurrentConfig.addQualifier(lang); |
| mCurrentConfig.addQualifier(region); |
| mCurrentConfig.addQualifier(version); |
| |
| if (mListener != null) { |
| mListener.onConfigurationChange(); |
| } |
| } |
| } |
| |
| private void onThemeChange() { |
| int themeIndex = mThemeCombo.getSelectionIndex(); |
| if (themeIndex != -1) { |
| String theme = mThemeCombo.getItem(themeIndex); |
| |
| if (theme.equals(THEME_SEPARATOR)) { |
| mThemeCombo.select(0); |
| } |
| |
| if (mListener != null) { |
| mListener.onThemeChange(); |
| } |
| } |
| } |
| |
| protected void OnClippingChange() { |
| mClipping = mClippingButton.getSelection(); |
| if (mListener != null) { |
| mListener.OnClippingChange(); |
| } |
| } |
| |
| /** |
| * 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. |
| * @return True if the given <var>style</var> is a theme. |
| */ |
| private boolean isTheme(IResourceValue value, Map<String, IResourceValue> styleMap) { |
| if (value instanceof IStyleResourceValue) { |
| IStyleResourceValue style = (IStyleResourceValue)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:")) { |
| frameworkStyle = true; |
| parentStyle = parentStyle.substring("android:".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. |
| value = styleMap.get(parentStyle); |
| if (value != null) { |
| return isTheme(value, styleMap); |
| } |
| } |
| } |
| } |
| |
| return false; |
| } |
| } |
| |