Home | History | Annotate | Download | only in gle2
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.eclipse.org/org/documents/epl-v10.php
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
     18 
     19 import static com.android.SdkConstants.DOT_PNG;
     20 import static com.android.SdkConstants.FQCN_DATE_PICKER;
     21 import static com.android.SdkConstants.FQCN_EXPANDABLE_LIST_VIEW;
     22 import static com.android.SdkConstants.FQCN_LIST_VIEW;
     23 import static com.android.SdkConstants.FQCN_TIME_PICKER;
     24 
     25 import com.android.annotations.NonNull;
     26 import com.android.annotations.Nullable;
     27 import com.android.ide.common.rendering.LayoutLibrary;
     28 import com.android.ide.common.rendering.api.Capability;
     29 import com.android.ide.common.rendering.api.RenderSession;
     30 import com.android.ide.common.rendering.api.ResourceValue;
     31 import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
     32 import com.android.ide.common.rendering.api.StyleResourceValue;
     33 import com.android.ide.common.rendering.api.ViewInfo;
     34 import com.android.ide.common.resources.ResourceResolver;
     35 import com.android.ide.eclipse.adt.AdtPlugin;
     36 import com.android.ide.eclipse.adt.internal.editors.descriptors.DocumentDescriptor;
     37 import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
     38 import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
     39 import com.android.ide.eclipse.adt.internal.editors.layout.gre.PaletteMetadataDescriptor;
     40 import com.android.ide.eclipse.adt.internal.editors.layout.gre.ViewMetadataRepository;
     41 import com.android.ide.eclipse.adt.internal.editors.layout.gre.ViewMetadataRepository.RenderMode;
     42 import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode;
     43 import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
     44 import com.android.ide.eclipse.adt.internal.resources.ResourceHelper;
     45 import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
     46 import com.android.sdklib.IAndroidTarget;
     47 import com.android.utils.Pair;
     48 
     49 import org.eclipse.core.runtime.IPath;
     50 import org.eclipse.core.runtime.IStatus;
     51 import org.eclipse.jface.resource.ImageDescriptor;
     52 import org.eclipse.swt.graphics.RGB;
     53 import org.w3c.dom.Document;
     54 import org.w3c.dom.Element;
     55 import org.w3c.dom.Node;
     56 import org.w3c.dom.NodeList;
     57 
     58 import java.awt.image.BufferedImage;
     59 import java.io.BufferedInputStream;
     60 import java.io.File;
     61 import java.io.FileInputStream;
     62 import java.io.IOException;
     63 import java.io.InputStream;
     64 import java.net.MalformedURLException;
     65 import java.util.ArrayList;
     66 import java.util.Collections;
     67 import java.util.List;
     68 import java.util.Properties;
     69 
     70 import javax.imageio.ImageIO;
     71 
     72 /**
     73  * Factory which can provide preview icons for android views of a particular SDK and
     74  * editor's configuration chooser
     75  */
     76 public class PreviewIconFactory {
     77     private PaletteControl mPalette;
     78     private RGB mBackground;
     79     private RGB mForeground;
     80     private File mImageDir;
     81 
     82     private static final String PREVIEW_INFO_FILE = "preview.properties"; //$NON-NLS-1$
     83 
     84     public PreviewIconFactory(PaletteControl palette) {
     85         mPalette = palette;
     86     }
     87 
     88     /**
     89      * Resets the state in the preview icon factory such that it will re-fetch information
     90      * like the theme and SDK (the icons themselves are cached in a directory across IDE
     91      * session though)
     92      */
     93     public void reset() {
     94         mImageDir = null;
     95         mBackground = null;
     96         mForeground = null;
     97     }
     98 
     99     /**
    100      * Deletes all the persistent state for the current settings such that it will be regenerated
    101      */
    102     public void refresh() {
    103         File imageDir = getImageDir(false);
    104         if (imageDir != null && imageDir.exists()) {
    105             File[] files = imageDir.listFiles();
    106             for (File file : files) {
    107                 file.delete();
    108             }
    109             imageDir.delete();
    110             reset();
    111         }
    112     }
    113 
    114     /**
    115      * Returns an image descriptor for the given element descriptor, or null if no image
    116      * could be computed. The rendering parameters (SDK, theme etc) correspond to those
    117      * stored in the associated palette.
    118      *
    119      * @param desc the element descriptor to get an image for
    120      * @return an image descriptor, or null if no image could be rendered
    121      */
    122     public ImageDescriptor getImageDescriptor(ElementDescriptor desc) {
    123         File imageDir = getImageDir(false);
    124         if (!imageDir.exists()) {
    125             render();
    126         }
    127         File file = new File(imageDir, getFileName(desc));
    128         if (file.exists()) {
    129             try {
    130                 return ImageDescriptor.createFromURL(file.toURI().toURL());
    131             } catch (MalformedURLException e) {
    132                 AdtPlugin.log(e, "Could not create image descriptor for %s", file);
    133             }
    134         }
    135 
    136         return null;
    137     }
    138 
    139     /**
    140      * Partition the elements in the document according to their rendering preferences;
    141      * elements that should be skipped are removed, elements that should be rendered alone
    142      * are placed in their own list, etc
    143      *
    144      * @param document the document containing render fragments for the various elements
    145      * @return
    146      */
    147     private List<List<Element>> partitionRenderElements(Document document) {
    148         List<List<Element>> elements = new ArrayList<List<Element>>();
    149 
    150         List<Element> shared = new ArrayList<Element>();
    151         Element root = document.getDocumentElement();
    152         elements.add(shared);
    153 
    154         ViewMetadataRepository repository = ViewMetadataRepository.get();
    155 
    156         NodeList children = root.getChildNodes();
    157         for (int i = 0, n = children.getLength(); i < n; i++) {
    158             Node node = children.item(i);
    159             if (node.getNodeType() == Node.ELEMENT_NODE) {
    160                 Element element = (Element) node;
    161                 String fqn = repository.getFullClassName(element);
    162                 assert fqn.length() > 0 : element.getNodeName();
    163                 RenderMode renderMode = repository.getRenderMode(fqn);
    164 
    165                 // Temporary special cases
    166                 if (fqn.equals(FQCN_LIST_VIEW) || fqn.equals(FQCN_EXPANDABLE_LIST_VIEW)) {
    167                     if (!mPalette.getEditor().renderingSupports(Capability.ADAPTER_BINDING)) {
    168                         renderMode = RenderMode.SKIP;
    169                     }
    170                 } else if (fqn.equals(FQCN_DATE_PICKER) || fqn.equals(FQCN_TIME_PICKER)) {
    171                     IAndroidTarget renderingTarget = mPalette.getEditor().getRenderingTarget();
    172                     // In Honeycomb, these widgets only render properly in the Holo themes.
    173                     int apiLevel = renderingTarget.getVersion().getApiLevel();
    174                     if (apiLevel == 11) {
    175                         String themeName = mPalette.getCurrentTheme();
    176                         if (themeName == null || !themeName.startsWith("Theme.Holo")) { //$NON-NLS-1$
    177                             // Note - it's possible that the the theme is some other theme
    178                             // such as a user theme which inherits from Theme.Holo and that
    179                             // the render -would- have worked, but it's harder to detect that
    180                             // scenario, so we err on the side of caution and just show an
    181                             // icon + name for the time widgets.
    182                             renderMode = RenderMode.SKIP;
    183                         }
    184                     } else if (apiLevel >= 12) {
    185                         // Currently broken, even for Holo.
    186                         renderMode = RenderMode.SKIP;
    187                     } // apiLevel <= 10 is fine
    188                 }
    189 
    190                 if (renderMode == RenderMode.ALONE) {
    191                     elements.add(Collections.singletonList(element));
    192                 } else if (renderMode == RenderMode.NORMAL) {
    193                     shared.add(element);
    194                 } else {
    195                     assert renderMode == RenderMode.SKIP;
    196                 }
    197             }
    198         }
    199 
    200         return elements;
    201     }
    202 
    203     /**
    204      * Renders ALL the widgets and then extracts image data for each view and saves it on
    205      * disk
    206      */
    207     private boolean render() {
    208         File imageDir = getImageDir(true);
    209 
    210         GraphicalEditorPart editor = mPalette.getEditor();
    211         LayoutEditorDelegate layoutEditorDelegate = editor.getEditorDelegate();
    212         LayoutLibrary layoutLibrary = editor.getLayoutLibrary();
    213         Integer overrideBgColor = null;
    214         if (layoutLibrary != null) {
    215             if (layoutLibrary.supports(Capability.CUSTOM_BACKGROUND_COLOR)) {
    216                 Pair<RGB, RGB> themeColors = getColorsFromTheme();
    217                 RGB bg = themeColors.getFirst();
    218                 RGB fg = themeColors.getSecond();
    219                 if (bg != null) {
    220                     storeBackground(imageDir, bg, fg);
    221                     overrideBgColor = Integer.valueOf(ImageUtils.rgbToInt(bg, 0xFF));
    222                 }
    223             }
    224         }
    225 
    226         ViewMetadataRepository repository = ViewMetadataRepository.get();
    227         Document document = repository.getRenderingConfigDoc();
    228 
    229         if (document == null) {
    230             return false;
    231         }
    232 
    233         // Construct UI model from XML
    234         AndroidTargetData data = layoutEditorDelegate.getEditor().getTargetData();
    235         DocumentDescriptor documentDescriptor;
    236         if (data == null) {
    237             documentDescriptor = new DocumentDescriptor("temp", null/*children*/);//$NON-NLS-1$
    238         } else {
    239             documentDescriptor = data.getLayoutDescriptors().getDescriptor();
    240         }
    241         UiDocumentNode model = (UiDocumentNode) documentDescriptor.createUiNode();
    242         model.setEditor(layoutEditorDelegate.getEditor());
    243         model.setUnknownDescriptorProvider(editor.getModel().getUnknownDescriptorProvider());
    244 
    245         Element documentElement = document.getDocumentElement();
    246         List<List<Element>> elements = partitionRenderElements(document);
    247         for (List<Element> elementGroup : elements) {
    248             // Replace the document elements with the current element group
    249             while (documentElement.getFirstChild() != null) {
    250                 documentElement.removeChild(documentElement.getFirstChild());
    251             }
    252             for (Element element : elementGroup) {
    253                 documentElement.appendChild(element);
    254             }
    255 
    256             model.loadFromXmlNode(document);
    257 
    258             RenderSession session = null;
    259             NodeList childNodes = documentElement.getChildNodes();
    260             try {
    261                 // Important to get these sizes large enough for clients that don't support
    262                 // RenderMode.FULL_EXPAND such as 1.6
    263                 int width = 200;
    264                 int height = childNodes.getLength() == 1 ? 400 : 1600;
    265 
    266                 session = RenderService.create(editor)
    267                     .setModel(model)
    268                     .setOverrideRenderSize(width, height)
    269                     .setRenderingMode(RenderingMode.FULL_EXPAND)
    270                     .setLog(new RenderLogger("palette"))
    271                     .setOverrideBgColor(overrideBgColor)
    272                     .setDecorations(false)
    273                     .createRenderSession();
    274             } catch (Throwable t) {
    275                 // If there are internal errors previewing the components just revert to plain
    276                 // icons and labels
    277                 continue;
    278             }
    279 
    280             if (session != null) {
    281                 if (session.getResult().isSuccess()) {
    282                     BufferedImage image = session.getImage();
    283                     if (image != null && image.getWidth() > 0 && image.getHeight() > 0) {
    284 
    285                         // Fallback for older platforms where we couldn't do background rendering
    286                         // at the beginning of this method
    287                         if (mBackground == null) {
    288                             Pair<RGB, RGB> themeColors = getColorsFromTheme();
    289                             RGB bg = themeColors.getFirst();
    290                             RGB fg = themeColors.getSecond();
    291 
    292                             if (bg == null) {
    293                                 // Just use a pixel from the rendering instead.
    294                                 int p = image.getRGB(image.getWidth() - 1, image.getHeight() - 1);
    295                                 // However, in this case we don't trust the foreground color
    296                                 // even if one was found in the themes; pick one that is guaranteed
    297                                 // to contrast with the background
    298                                 bg = ImageUtils.intToRgb(p);
    299                                 if (ImageUtils.getBrightness(ImageUtils.rgbToInt(bg, 255)) < 128) {
    300                                     fg = new RGB(255, 255, 255);
    301                                 } else {
    302                                     fg = new RGB(0, 0, 0);
    303                                 }
    304                             }
    305                             storeBackground(imageDir, bg, fg);
    306                             assert mBackground != null;
    307                         }
    308 
    309                         List<ViewInfo> viewInfoList = session.getRootViews();
    310                         if (viewInfoList != null && viewInfoList.size() > 0) {
    311                             // We don't render previews under a <merge> so there should
    312                             // only be one root.
    313                             ViewInfo firstRoot = viewInfoList.get(0);
    314                             int parentX = firstRoot.getLeft();
    315                             int parentY = firstRoot.getTop();
    316                             List<ViewInfo> infos = firstRoot.getChildren();
    317                             for (ViewInfo info : infos) {
    318                                 Object cookie = info.getCookie();
    319                                 if (!(cookie instanceof UiElementNode)) {
    320                                     continue;
    321                                 }
    322                                 UiElementNode node = (UiElementNode) cookie;
    323                                 String fileName = getFileName(node);
    324                                 File file = new File(imageDir, fileName);
    325                                 if (file.exists()) {
    326                                     // On Windows, perhaps we need to rename instead?
    327                                     file.delete();
    328                                 }
    329                                 int x1 = parentX + info.getLeft();
    330                                 int y1 = parentY + info.getTop();
    331                                 int x2 = parentX + info.getRight();
    332                                 int y2 = parentY + info.getBottom();
    333                                 if (x1 != x2 && y1 != y2) {
    334                                     savePreview(file, image, x1, y1, x2, y2);
    335                                 }
    336                             }
    337                         }
    338                     }
    339                 } else {
    340                     StringBuilder sb = new StringBuilder();
    341                     for (int i = 0, n = childNodes.getLength(); i < n; i++) {
    342                         Node node = childNodes.item(i);
    343                         if (node instanceof Element) {
    344                             Element e = (Element) node;
    345                             String fqn = repository.getFullClassName(e);
    346                             fqn = fqn.substring(fqn.lastIndexOf('.') + 1);
    347                             if (sb.length() > 0) {
    348                                 sb.append(", "); //$NON-NLS-1$
    349                             }
    350                             sb.append(fqn);
    351                         }
    352                     }
    353                     AdtPlugin.log(IStatus.WARNING, "Failed to render set of icons for %1$s",
    354                             sb.toString());
    355 
    356                     if (session.getResult().getException() != null) {
    357                         AdtPlugin.log(session.getResult().getException(),
    358                                 session.getResult().getErrorMessage());
    359                     } else if (session.getResult().getErrorMessage() != null) {
    360                         AdtPlugin.log(IStatus.WARNING, session.getResult().getErrorMessage());
    361                     }
    362                 }
    363 
    364                 session.dispose();
    365             }
    366         }
    367 
    368         mPalette.getEditor().recomputeLayout();
    369 
    370         return true;
    371     }
    372 
    373     /**
    374      * Look up the background and foreground colors from the theme. May not find either
    375      * the background or foreground or both, but will always return a pair of possibly
    376      * null colors.
    377      *
    378      * @return a pair of possibly null color descriptions
    379      */
    380     @NonNull
    381     private Pair<RGB, RGB> getColorsFromTheme() {
    382         RGB background = null;
    383         RGB foreground = null;
    384 
    385         ResourceResolver resources = mPalette.getEditor().getResourceResolver();
    386         if (resources == null) {
    387             return Pair.of(background, foreground);
    388         }
    389         StyleResourceValue theme = resources.getCurrentTheme();
    390         if (theme != null) {
    391             background = resolveThemeColor(resources, "windowBackground"); //$NON-NLS-1$
    392             if (background == null) {
    393                 background = renderDrawableResource("windowBackground"); //$NON-NLS-1$
    394                 // This causes some harm with some themes: We'll find a color, say black,
    395                 // that isn't actually rendered in the theme. Better to use null here,
    396                 // which will cause the caller to pick a pixel from the observed background
    397                 // instead.
    398                 //if (background == null) {
    399                 //    background = resolveThemeColor(resources, "colorBackground"); //$NON-NLS-1$
    400                 //}
    401             }
    402             foreground = resolveThemeColor(resources, "textColorPrimary"); //$NON-NLS-1$
    403         }
    404 
    405         // Ensure that the foreground color is suitably distinct from the background color
    406         if (background != null) {
    407             int bgRgb = ImageUtils.rgbToInt(background, 0xFF);
    408             int backgroundBrightness = ImageUtils.getBrightness(bgRgb);
    409             if (foreground == null) {
    410                 if (backgroundBrightness < 128) {
    411                     foreground = new RGB(255, 255, 255);
    412                 } else {
    413                     foreground = new RGB(0, 0, 0);
    414                 }
    415             } else {
    416                 int fgRgb = ImageUtils.rgbToInt(foreground, 0xFF);
    417                 int foregroundBrightness = ImageUtils.getBrightness(fgRgb);
    418                 if (Math.abs(backgroundBrightness - foregroundBrightness) < 64) {
    419                     if (backgroundBrightness < 128) {
    420                         foreground = new RGB(255, 255, 255);
    421                     } else {
    422                         foreground = new RGB(0, 0, 0);
    423                     }
    424                 }
    425             }
    426         }
    427 
    428         return Pair.of(background, foreground);
    429     }
    430 
    431     /**
    432      * Renders the given resource which should refer to a drawable and returns a
    433      * representative color value for the drawable (such as the color in the center)
    434      *
    435      * @param themeItemName the item in the theme to be looked up and rendered
    436      * @return a color representing a typical color in the drawable
    437      */
    438     private RGB renderDrawableResource(String themeItemName) {
    439         GraphicalEditorPart editor = mPalette.getEditor();
    440         ResourceResolver resources = editor.getResourceResolver();
    441         ResourceValue resourceValue = resources.findItemInTheme(themeItemName);
    442         BufferedImage image = RenderService.create(editor)
    443             .setOverrideRenderSize(100, 100)
    444             .renderDrawable(resourceValue);
    445         if (image != null) {
    446             // Use the middle pixel as the color since that works better for gradients;
    447             // solid colors work too.
    448             int rgb = image.getRGB(image.getWidth() / 2, image.getHeight() / 2);
    449             return ImageUtils.intToRgb(rgb);
    450         }
    451 
    452         return null;
    453     }
    454 
    455     private static RGB resolveThemeColor(ResourceResolver resources, String resourceName) {
    456         ResourceValue textColor = resources.findItemInTheme(resourceName);
    457         return ResourceHelper.resolveColor(resources, textColor);
    458     }
    459 
    460     private String getFileName(ElementDescriptor descriptor) {
    461         if (descriptor instanceof PaletteMetadataDescriptor) {
    462             PaletteMetadataDescriptor pmd = (PaletteMetadataDescriptor) descriptor;
    463             StringBuilder sb = new StringBuilder();
    464             String name = pmd.getUiName();
    465             // Strip out whitespace, parentheses, etc.
    466             for (int i = 0, n = name.length(); i < n; i++) {
    467                 char c = name.charAt(i);
    468                 if (Character.isLetter(c)) {
    469                     sb.append(c);
    470                 }
    471             }
    472             return sb.toString() + DOT_PNG;
    473         }
    474         return descriptor.getUiName() + DOT_PNG;
    475     }
    476 
    477     private String getFileName(UiElementNode node) {
    478         ViewMetadataRepository repository = ViewMetadataRepository.get();
    479         String fqn = repository.getFullClassName((Element) node.getXmlNode());
    480         return fqn.substring(fqn.lastIndexOf('.') + 1) + DOT_PNG;
    481     }
    482 
    483     /**
    484      * Cleans up a name by removing punctuation and whitespace etc to make
    485      * it a better filename
    486      * @param name the name to clean
    487      * @return a cleaned up name
    488      */
    489     @NonNull
    490     private static String cleanup(@Nullable String name) {
    491         if (name == null) {
    492             return "";
    493         }
    494 
    495         // Extract just the characters (no whitespace, parentheses, punctuation etc)
    496         // to ensure that the filename is pretty portable
    497         StringBuilder sb = new StringBuilder(name.length());
    498         for (int i = 0; i < name.length(); i++) {
    499             char c = name.charAt(i);
    500             if (Character.isJavaIdentifierPart(c)) {
    501                 sb.append(Character.toLowerCase(c));
    502             }
    503         }
    504 
    505         return sb.toString();
    506     }
    507 
    508     /** Returns the location of a directory containing image previews (which may not exist) */
    509     private File getImageDir(boolean create) {
    510         if (mImageDir == null) {
    511             // Location for plugin-related state data
    512             IPath pluginState = AdtPlugin.getDefault().getStateLocation();
    513 
    514             // We have multiple directories - one for each combination of SDK, theme and device
    515             // (and later, possibly other qualifiers).
    516             // These are created -lazily-.
    517             String targetName = mPalette.getCurrentTarget().hashString();
    518             String androidTargetNamePrefix = "android-";
    519             String themeNamePrefix = "Theme.";
    520             if (targetName.startsWith(androidTargetNamePrefix)) {
    521                 targetName = targetName.substring(androidTargetNamePrefix.length());
    522             }
    523             String themeName = mPalette.getCurrentTheme();
    524             if (themeName == null) {
    525                 themeName = "Theme"; //$NON-NLS-1$
    526             }
    527             if (themeName.startsWith(themeNamePrefix)) {
    528                 themeName = themeName.substring(themeNamePrefix.length());
    529             }
    530             targetName = cleanup(targetName);
    531             themeName = cleanup(themeName);
    532             String deviceName = cleanup(mPalette.getCurrentDevice());
    533             String dirName = String.format("palette-preview-r16b-%s-%s-%s", targetName,
    534                     themeName, deviceName);
    535             IPath dirPath = pluginState.append(dirName);
    536 
    537             mImageDir = new File(dirPath.toOSString());
    538         }
    539 
    540         if (create && !mImageDir.exists()) {
    541             mImageDir.mkdirs();
    542         }
    543 
    544         return mImageDir;
    545     }
    546 
    547     private void savePreview(File output, BufferedImage image,
    548             int left, int top, int right, int bottom) {
    549         try {
    550             BufferedImage im = ImageUtils.subImage(image, left, top, right, bottom);
    551             ImageIO.write(im, "PNG", output); //$NON-NLS-1$
    552         } catch (IOException e) {
    553             AdtPlugin.log(e, "Failed writing palette file");
    554         }
    555     }
    556 
    557     private void storeBackground(File imageDir, RGB bg, RGB fg) {
    558         mBackground = bg;
    559         mForeground = fg;
    560         File file = new File(imageDir, PREVIEW_INFO_FILE);
    561         String colors = String.format(
    562                 "background=#%02x%02x%02x\nforeground=#%02x%02x%02x\n", //$NON-NLS-1$
    563                 bg.red, bg.green, bg.blue,
    564                 fg.red, fg.green, fg.blue);
    565         AdtPlugin.writeFile(file, colors);
    566     }
    567 
    568     public RGB getBackgroundColor() {
    569         if (mBackground == null) {
    570             initColors();
    571         }
    572 
    573         return mBackground;
    574     }
    575 
    576     public RGB getForegroundColor() {
    577         if (mForeground == null) {
    578             initColors();
    579         }
    580 
    581         return mForeground;
    582     }
    583 
    584     public void initColors() {
    585         try {
    586             // Already initialized? Foreground can be null which would call
    587             // initColors again and again, but background is never null after
    588             // initialization so we use it as the have-initialized flag.
    589             if (mBackground != null) {
    590                 return;
    591             }
    592 
    593             File imageDir = getImageDir(false);
    594             if (!imageDir.exists()) {
    595                 render();
    596 
    597                 // Initialized as part of the render
    598                 if (mBackground != null) {
    599                     return;
    600                 }
    601             }
    602 
    603             File file = new File(imageDir, PREVIEW_INFO_FILE);
    604             if (file.exists()) {
    605                 Properties properties = new Properties();
    606                 InputStream is = null;
    607                 try {
    608                     is = new BufferedInputStream(new FileInputStream(file));
    609                     properties.load(is);
    610                 } catch (IOException e) {
    611                     AdtPlugin.log(e, "Can't read preview properties");
    612                 } finally {
    613                     if (is != null) {
    614                         try {
    615                             is.close();
    616                         } catch (IOException e) {
    617                             // Nothing useful can be done.
    618                         }
    619                     }
    620                 }
    621 
    622                 String colorString = (String) properties.get("background"); //$NON-NLS-1$
    623                 if (colorString != null) {
    624                     int rgb = ImageUtils.getColor(colorString.trim());
    625                     mBackground = ImageUtils.intToRgb(rgb);
    626                 }
    627                 colorString = (String) properties.get("foreground"); //$NON-NLS-1$
    628                 if (colorString != null) {
    629                     int rgb = ImageUtils.getColor(colorString.trim());
    630                     mForeground = ImageUtils.intToRgb(rgb);
    631                 }
    632             }
    633 
    634             if (mBackground == null) {
    635                 mBackground = new RGB(0, 0, 0);
    636             }
    637             // mForeground is allowed to be null.
    638         } catch (Throwable t) {
    639             AdtPlugin.log(t, "Cannot initialize preview color settings");
    640         }
    641     }
    642 }
    643