aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PreviewIconFactory.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/gle2/PreviewIconFactory.java')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PreviewIconFactory.java642
1 files changed, 642 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PreviewIconFactory.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PreviewIconFactory.java
new file mode 100644
index 000000000..5661b2919
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PreviewIconFactory.java
@@ -0,0 +1,642 @@
+/*
+ * Copyright (C) 2011 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.gle2;
+
+import static com.android.SdkConstants.DOT_PNG;
+import static com.android.SdkConstants.FQCN_DATE_PICKER;
+import static com.android.SdkConstants.FQCN_EXPANDABLE_LIST_VIEW;
+import static com.android.SdkConstants.FQCN_LIST_VIEW;
+import static com.android.SdkConstants.FQCN_TIME_PICKER;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.ide.common.rendering.LayoutLibrary;
+import com.android.ide.common.rendering.api.Capability;
+import com.android.ide.common.rendering.api.RenderSession;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
+import com.android.ide.common.rendering.api.StyleResourceValue;
+import com.android.ide.common.rendering.api.ViewInfo;
+import com.android.ide.common.resources.ResourceResolver;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.DocumentDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
+import com.android.ide.eclipse.adt.internal.editors.layout.gre.PaletteMetadataDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.layout.gre.ViewMetadataRepository;
+import com.android.ide.eclipse.adt.internal.editors.layout.gre.ViewMetadataRepository.RenderMode;
+import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode;
+import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
+import com.android.ide.eclipse.adt.internal.resources.ResourceHelper;
+import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
+import com.android.sdklib.IAndroidTarget;
+import com.android.utils.Pair;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.graphics.RGB;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.awt.image.BufferedImage;
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Properties;
+
+import javax.imageio.ImageIO;
+
+/**
+ * Factory which can provide preview icons for android views of a particular SDK and
+ * editor's configuration chooser
+ */
+public class PreviewIconFactory {
+ private PaletteControl mPalette;
+ private RGB mBackground;
+ private RGB mForeground;
+ private File mImageDir;
+
+ private static final String PREVIEW_INFO_FILE = "preview.properties"; //$NON-NLS-1$
+
+ public PreviewIconFactory(PaletteControl palette) {
+ mPalette = palette;
+ }
+
+ /**
+ * Resets the state in the preview icon factory such that it will re-fetch information
+ * like the theme and SDK (the icons themselves are cached in a directory across IDE
+ * session though)
+ */
+ public void reset() {
+ mImageDir = null;
+ mBackground = null;
+ mForeground = null;
+ }
+
+ /**
+ * Deletes all the persistent state for the current settings such that it will be regenerated
+ */
+ public void refresh() {
+ File imageDir = getImageDir(false);
+ if (imageDir != null && imageDir.exists()) {
+ File[] files = imageDir.listFiles();
+ for (File file : files) {
+ file.delete();
+ }
+ imageDir.delete();
+ reset();
+ }
+ }
+
+ /**
+ * Returns an image descriptor for the given element descriptor, or null if no image
+ * could be computed. The rendering parameters (SDK, theme etc) correspond to those
+ * stored in the associated palette.
+ *
+ * @param desc the element descriptor to get an image for
+ * @return an image descriptor, or null if no image could be rendered
+ */
+ public ImageDescriptor getImageDescriptor(ElementDescriptor desc) {
+ File imageDir = getImageDir(false);
+ if (!imageDir.exists()) {
+ render();
+ }
+ File file = new File(imageDir, getFileName(desc));
+ if (file.exists()) {
+ try {
+ return ImageDescriptor.createFromURL(file.toURI().toURL());
+ } catch (MalformedURLException e) {
+ AdtPlugin.log(e, "Could not create image descriptor for %s", file);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Partition the elements in the document according to their rendering preferences;
+ * elements that should be skipped are removed, elements that should be rendered alone
+ * are placed in their own list, etc
+ *
+ * @param document the document containing render fragments for the various elements
+ * @return
+ */
+ private List<List<Element>> partitionRenderElements(Document document) {
+ List<List<Element>> elements = new ArrayList<List<Element>>();
+
+ List<Element> shared = new ArrayList<Element>();
+ Element root = document.getDocumentElement();
+ elements.add(shared);
+
+ ViewMetadataRepository repository = ViewMetadataRepository.get();
+
+ NodeList children = root.getChildNodes();
+ for (int i = 0, n = children.getLength(); i < n; i++) {
+ Node node = children.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ Element element = (Element) node;
+ String fqn = repository.getFullClassName(element);
+ assert fqn.length() > 0 : element.getNodeName();
+ RenderMode renderMode = repository.getRenderMode(fqn);
+
+ // Temporary special cases
+ if (fqn.equals(FQCN_LIST_VIEW) || fqn.equals(FQCN_EXPANDABLE_LIST_VIEW)) {
+ if (!mPalette.getEditor().renderingSupports(Capability.ADAPTER_BINDING)) {
+ renderMode = RenderMode.SKIP;
+ }
+ } else if (fqn.equals(FQCN_DATE_PICKER) || fqn.equals(FQCN_TIME_PICKER)) {
+ IAndroidTarget renderingTarget = mPalette.getEditor().getRenderingTarget();
+ // In Honeycomb, these widgets only render properly in the Holo themes.
+ int apiLevel = renderingTarget.getVersion().getApiLevel();
+ if (apiLevel == 11) {
+ String themeName = mPalette.getCurrentTheme();
+ if (themeName == null || !themeName.startsWith("Theme.Holo")) { //$NON-NLS-1$
+ // Note - it's possible that the the theme is some other theme
+ // such as a user theme which inherits from Theme.Holo and that
+ // the render -would- have worked, but it's harder to detect that
+ // scenario, so we err on the side of caution and just show an
+ // icon + name for the time widgets.
+ renderMode = RenderMode.SKIP;
+ }
+ } else if (apiLevel >= 12) {
+ // Currently broken, even for Holo.
+ renderMode = RenderMode.SKIP;
+ } // apiLevel <= 10 is fine
+ }
+
+ if (renderMode == RenderMode.ALONE) {
+ elements.add(Collections.singletonList(element));
+ } else if (renderMode == RenderMode.NORMAL) {
+ shared.add(element);
+ } else {
+ assert renderMode == RenderMode.SKIP;
+ }
+ }
+ }
+
+ return elements;
+ }
+
+ /**
+ * Renders ALL the widgets and then extracts image data for each view and saves it on
+ * disk
+ */
+ private boolean render() {
+ File imageDir = getImageDir(true);
+
+ GraphicalEditorPart editor = mPalette.getEditor();
+ LayoutEditorDelegate layoutEditorDelegate = editor.getEditorDelegate();
+ LayoutLibrary layoutLibrary = editor.getLayoutLibrary();
+ Integer overrideBgColor = null;
+ if (layoutLibrary != null) {
+ if (layoutLibrary.supports(Capability.CUSTOM_BACKGROUND_COLOR)) {
+ Pair<RGB, RGB> themeColors = getColorsFromTheme();
+ RGB bg = themeColors.getFirst();
+ RGB fg = themeColors.getSecond();
+ if (bg != null) {
+ storeBackground(imageDir, bg, fg);
+ overrideBgColor = Integer.valueOf(ImageUtils.rgbToInt(bg, 0xFF));
+ }
+ }
+ }
+
+ ViewMetadataRepository repository = ViewMetadataRepository.get();
+ Document document = repository.getRenderingConfigDoc();
+
+ if (document == null) {
+ return false;
+ }
+
+ // Construct UI model from XML
+ AndroidTargetData data = layoutEditorDelegate.getEditor().getTargetData();
+ DocumentDescriptor documentDescriptor;
+ if (data == null) {
+ documentDescriptor = new DocumentDescriptor("temp", null/*children*/);//$NON-NLS-1$
+ } else {
+ documentDescriptor = data.getLayoutDescriptors().getDescriptor();
+ }
+ UiDocumentNode model = (UiDocumentNode) documentDescriptor.createUiNode();
+ model.setEditor(layoutEditorDelegate.getEditor());
+ model.setUnknownDescriptorProvider(editor.getModel().getUnknownDescriptorProvider());
+
+ Element documentElement = document.getDocumentElement();
+ List<List<Element>> elements = partitionRenderElements(document);
+ for (List<Element> elementGroup : elements) {
+ // Replace the document elements with the current element group
+ while (documentElement.getFirstChild() != null) {
+ documentElement.removeChild(documentElement.getFirstChild());
+ }
+ for (Element element : elementGroup) {
+ documentElement.appendChild(element);
+ }
+
+ model.loadFromXmlNode(document);
+
+ RenderSession session = null;
+ NodeList childNodes = documentElement.getChildNodes();
+ try {
+ // Important to get these sizes large enough for clients that don't support
+ // RenderMode.FULL_EXPAND such as 1.6
+ int width = 200;
+ int height = childNodes.getLength() == 1 ? 400 : 1600;
+
+ session = RenderService.create(editor)
+ .setModel(model)
+ .setOverrideRenderSize(width, height)
+ .setRenderingMode(RenderingMode.FULL_EXPAND)
+ .setLog(editor.createRenderLogger("palette"))
+ .setOverrideBgColor(overrideBgColor)
+ .setDecorations(false)
+ .createRenderSession();
+ } catch (Throwable t) {
+ // If there are internal errors previewing the components just revert to plain
+ // icons and labels
+ continue;
+ }
+
+ if (session != null) {
+ if (session.getResult().isSuccess()) {
+ BufferedImage image = session.getImage();
+ if (image != null && image.getWidth() > 0 && image.getHeight() > 0) {
+
+ // Fallback for older platforms where we couldn't do background rendering
+ // at the beginning of this method
+ if (mBackground == null) {
+ Pair<RGB, RGB> themeColors = getColorsFromTheme();
+ RGB bg = themeColors.getFirst();
+ RGB fg = themeColors.getSecond();
+
+ if (bg == null) {
+ // Just use a pixel from the rendering instead.
+ int p = image.getRGB(image.getWidth() - 1, image.getHeight() - 1);
+ // However, in this case we don't trust the foreground color
+ // even if one was found in the themes; pick one that is guaranteed
+ // to contrast with the background
+ bg = ImageUtils.intToRgb(p);
+ if (ImageUtils.getBrightness(ImageUtils.rgbToInt(bg, 255)) < 128) {
+ fg = new RGB(255, 255, 255);
+ } else {
+ fg = new RGB(0, 0, 0);
+ }
+ }
+ storeBackground(imageDir, bg, fg);
+ assert mBackground != null;
+ }
+
+ List<ViewInfo> viewInfoList = session.getRootViews();
+ if (viewInfoList != null && viewInfoList.size() > 0) {
+ // We don't render previews under a <merge> so there should
+ // only be one root.
+ ViewInfo firstRoot = viewInfoList.get(0);
+ int parentX = firstRoot.getLeft();
+ int parentY = firstRoot.getTop();
+ List<ViewInfo> infos = firstRoot.getChildren();
+ for (ViewInfo info : infos) {
+ Object cookie = info.getCookie();
+ if (!(cookie instanceof UiElementNode)) {
+ continue;
+ }
+ UiElementNode node = (UiElementNode) cookie;
+ String fileName = getFileName(node);
+ File file = new File(imageDir, fileName);
+ if (file.exists()) {
+ // On Windows, perhaps we need to rename instead?
+ file.delete();
+ }
+ int x1 = parentX + info.getLeft();
+ int y1 = parentY + info.getTop();
+ int x2 = parentX + info.getRight();
+ int y2 = parentY + info.getBottom();
+ if (x1 != x2 && y1 != y2) {
+ savePreview(file, image, x1, y1, x2, y2);
+ }
+ }
+ }
+ }
+ } else {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0, n = childNodes.getLength(); i < n; i++) {
+ Node node = childNodes.item(i);
+ if (node instanceof Element) {
+ Element e = (Element) node;
+ String fqn = repository.getFullClassName(e);
+ fqn = fqn.substring(fqn.lastIndexOf('.') + 1);
+ if (sb.length() > 0) {
+ sb.append(", "); //$NON-NLS-1$
+ }
+ sb.append(fqn);
+ }
+ }
+ AdtPlugin.log(IStatus.WARNING, "Failed to render set of icons for %1$s",
+ sb.toString());
+
+ if (session.getResult().getException() != null) {
+ AdtPlugin.log(session.getResult().getException(),
+ session.getResult().getErrorMessage());
+ } else if (session.getResult().getErrorMessage() != null) {
+ AdtPlugin.log(IStatus.WARNING, session.getResult().getErrorMessage());
+ }
+ }
+
+ session.dispose();
+ }
+ }
+
+ mPalette.getEditor().recomputeLayout();
+
+ return true;
+ }
+
+ /**
+ * Look up the background and foreground colors from the theme. May not find either
+ * the background or foreground or both, but will always return a pair of possibly
+ * null colors.
+ *
+ * @return a pair of possibly null color descriptions
+ */
+ @NonNull
+ private Pair<RGB, RGB> getColorsFromTheme() {
+ RGB background = null;
+ RGB foreground = null;
+
+ ResourceResolver resources = mPalette.getEditor().getResourceResolver();
+ if (resources == null) {
+ return Pair.of(background, foreground);
+ }
+ StyleResourceValue theme = resources.getCurrentTheme();
+ if (theme != null) {
+ background = resolveThemeColor(resources, "windowBackground"); //$NON-NLS-1$
+ if (background == null) {
+ background = renderDrawableResource("windowBackground"); //$NON-NLS-1$
+ // This causes some harm with some themes: We'll find a color, say black,
+ // that isn't actually rendered in the theme. Better to use null here,
+ // which will cause the caller to pick a pixel from the observed background
+ // instead.
+ //if (background == null) {
+ // background = resolveThemeColor(resources, "colorBackground"); //$NON-NLS-1$
+ //}
+ }
+ foreground = resolveThemeColor(resources, "textColorPrimary"); //$NON-NLS-1$
+ }
+
+ // Ensure that the foreground color is suitably distinct from the background color
+ if (background != null) {
+ int bgRgb = ImageUtils.rgbToInt(background, 0xFF);
+ int backgroundBrightness = ImageUtils.getBrightness(bgRgb);
+ if (foreground == null) {
+ if (backgroundBrightness < 128) {
+ foreground = new RGB(255, 255, 255);
+ } else {
+ foreground = new RGB(0, 0, 0);
+ }
+ } else {
+ int fgRgb = ImageUtils.rgbToInt(foreground, 0xFF);
+ int foregroundBrightness = ImageUtils.getBrightness(fgRgb);
+ if (Math.abs(backgroundBrightness - foregroundBrightness) < 64) {
+ if (backgroundBrightness < 128) {
+ foreground = new RGB(255, 255, 255);
+ } else {
+ foreground = new RGB(0, 0, 0);
+ }
+ }
+ }
+ }
+
+ return Pair.of(background, foreground);
+ }
+
+ /**
+ * Renders the given resource which should refer to a drawable and returns a
+ * representative color value for the drawable (such as the color in the center)
+ *
+ * @param themeItemName the item in the theme to be looked up and rendered
+ * @return a color representing a typical color in the drawable
+ */
+ private RGB renderDrawableResource(String themeItemName) {
+ GraphicalEditorPart editor = mPalette.getEditor();
+ ResourceResolver resources = editor.getResourceResolver();
+ ResourceValue resourceValue = resources.findItemInTheme(themeItemName);
+ BufferedImage image = RenderService.create(editor)
+ .setOverrideRenderSize(100, 100)
+ .renderDrawable(resourceValue);
+ if (image != null) {
+ // Use the middle pixel as the color since that works better for gradients;
+ // solid colors work too.
+ int rgb = image.getRGB(image.getWidth() / 2, image.getHeight() / 2);
+ return ImageUtils.intToRgb(rgb);
+ }
+
+ return null;
+ }
+
+ private static RGB resolveThemeColor(ResourceResolver resources, String resourceName) {
+ ResourceValue textColor = resources.findItemInTheme(resourceName);
+ return ResourceHelper.resolveColor(resources, textColor);
+ }
+
+ private String getFileName(ElementDescriptor descriptor) {
+ if (descriptor instanceof PaletteMetadataDescriptor) {
+ PaletteMetadataDescriptor pmd = (PaletteMetadataDescriptor) descriptor;
+ StringBuilder sb = new StringBuilder();
+ String name = pmd.getUiName();
+ // Strip out whitespace, parentheses, etc.
+ for (int i = 0, n = name.length(); i < n; i++) {
+ char c = name.charAt(i);
+ if (Character.isLetter(c)) {
+ sb.append(c);
+ }
+ }
+ return sb.toString() + DOT_PNG;
+ }
+ return descriptor.getUiName() + DOT_PNG;
+ }
+
+ private String getFileName(UiElementNode node) {
+ ViewMetadataRepository repository = ViewMetadataRepository.get();
+ String fqn = repository.getFullClassName((Element) node.getXmlNode());
+ return fqn.substring(fqn.lastIndexOf('.') + 1) + DOT_PNG;
+ }
+
+ /**
+ * Cleans up a name by removing punctuation and whitespace etc to make
+ * it a better filename
+ * @param name the name to clean
+ * @return a cleaned up name
+ */
+ @NonNull
+ private static String cleanup(@Nullable String name) {
+ if (name == null) {
+ return "";
+ }
+
+ // Extract just the characters (no whitespace, parentheses, punctuation etc)
+ // to ensure that the filename is pretty portable
+ StringBuilder sb = new StringBuilder(name.length());
+ for (int i = 0; i < name.length(); i++) {
+ char c = name.charAt(i);
+ if (Character.isJavaIdentifierPart(c)) {
+ sb.append(Character.toLowerCase(c));
+ }
+ }
+
+ return sb.toString();
+ }
+
+ /** Returns the location of a directory containing image previews (which may not exist) */
+ private File getImageDir(boolean create) {
+ if (mImageDir == null) {
+ // Location for plugin-related state data
+ IPath pluginState = AdtPlugin.getDefault().getStateLocation();
+
+ // We have multiple directories - one for each combination of SDK, theme and device
+ // (and later, possibly other qualifiers).
+ // These are created -lazily-.
+ String targetName = mPalette.getCurrentTarget().hashString();
+ String androidTargetNamePrefix = "android-";
+ String themeNamePrefix = "Theme.";
+ if (targetName.startsWith(androidTargetNamePrefix)) {
+ targetName = targetName.substring(androidTargetNamePrefix.length());
+ }
+ String themeName = mPalette.getCurrentTheme();
+ if (themeName == null) {
+ themeName = "Theme"; //$NON-NLS-1$
+ }
+ if (themeName.startsWith(themeNamePrefix)) {
+ themeName = themeName.substring(themeNamePrefix.length());
+ }
+ targetName = cleanup(targetName);
+ themeName = cleanup(themeName);
+ String deviceName = cleanup(mPalette.getCurrentDevice());
+ String dirName = String.format("palette-preview-r16b-%s-%s-%s", targetName,
+ themeName, deviceName);
+ IPath dirPath = pluginState.append(dirName);
+
+ mImageDir = new File(dirPath.toOSString());
+ }
+
+ if (create && !mImageDir.exists()) {
+ mImageDir.mkdirs();
+ }
+
+ return mImageDir;
+ }
+
+ private void savePreview(File output, BufferedImage image,
+ int left, int top, int right, int bottom) {
+ try {
+ BufferedImage im = ImageUtils.subImage(image, left, top, right, bottom);
+ ImageIO.write(im, "PNG", output); //$NON-NLS-1$
+ } catch (IOException e) {
+ AdtPlugin.log(e, "Failed writing palette file");
+ }
+ }
+
+ private void storeBackground(File imageDir, RGB bg, RGB fg) {
+ mBackground = bg;
+ mForeground = fg;
+ File file = new File(imageDir, PREVIEW_INFO_FILE);
+ String colors = String.format(
+ "background=#%02x%02x%02x\nforeground=#%02x%02x%02x\n", //$NON-NLS-1$
+ bg.red, bg.green, bg.blue,
+ fg.red, fg.green, fg.blue);
+ AdtPlugin.writeFile(file, colors);
+ }
+
+ public RGB getBackgroundColor() {
+ if (mBackground == null) {
+ initColors();
+ }
+
+ return mBackground;
+ }
+
+ public RGB getForegroundColor() {
+ if (mForeground == null) {
+ initColors();
+ }
+
+ return mForeground;
+ }
+
+ public void initColors() {
+ try {
+ // Already initialized? Foreground can be null which would call
+ // initColors again and again, but background is never null after
+ // initialization so we use it as the have-initialized flag.
+ if (mBackground != null) {
+ return;
+ }
+
+ File imageDir = getImageDir(false);
+ if (!imageDir.exists()) {
+ render();
+
+ // Initialized as part of the render
+ if (mBackground != null) {
+ return;
+ }
+ }
+
+ File file = new File(imageDir, PREVIEW_INFO_FILE);
+ if (file.exists()) {
+ Properties properties = new Properties();
+ InputStream is = null;
+ try {
+ is = new BufferedInputStream(new FileInputStream(file));
+ properties.load(is);
+ } catch (IOException e) {
+ AdtPlugin.log(e, "Can't read preview properties");
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ // Nothing useful can be done.
+ }
+ }
+ }
+
+ String colorString = (String) properties.get("background"); //$NON-NLS-1$
+ if (colorString != null) {
+ int rgb = ImageUtils.getColor(colorString.trim());
+ mBackground = ImageUtils.intToRgb(rgb);
+ }
+ colorString = (String) properties.get("foreground"); //$NON-NLS-1$
+ if (colorString != null) {
+ int rgb = ImageUtils.getColor(colorString.trim());
+ mForeground = ImageUtils.intToRgb(rgb);
+ }
+ }
+
+ if (mBackground == null) {
+ mBackground = new RGB(0, 0, 0);
+ }
+ // mForeground is allowed to be null.
+ } catch (Throwable t) {
+ AdtPlugin.log(t, "Cannot initialize preview color settings");
+ }
+ }
+}