diff options
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.java | 642 |
1 files changed, 0 insertions, 642 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 deleted file mode 100644 index 5661b2919..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PreviewIconFactory.java +++ /dev/null @@ -1,642 +0,0 @@ -/* - * 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"); - } - } -} |