aboutsummaryrefslogtreecommitdiffstats
path: root/ide_common/src/com/android/ide
diff options
context:
space:
mode:
authorXavier Ducrohet <xav@android.com>2010-12-09 19:40:29 -0800
committerXavier Ducrohet <xav@android.com>2010-12-10 11:30:54 -0800
commit79ebb8f4ec3236d361c4b6a6d97440a29c50aa4a (patch)
tree2b0a0b0047a5e9d7c5e794370db5a79163c75f03 /ide_common/src/com/android/ide
parent9a1f4d718be6c277b344ed361821e275858ebd11 (diff)
downloadsdk-79ebb8f4ec3236d361c4b6a6d97440a29c50aa4a.zip
sdk-79ebb8f4ec3236d361c4b6a6d97440a29c50aa4a.tar.gz
sdk-79ebb8f4ec3236d361c4b6a6d97440a29c50aa4a.tar.bz2
Clean up the api around Layoutlib.
Move (Style/DensityBased)ResourceValue into layoutlib_api and make the API use that instead of the interface. We'll get ride of the interfaces once only obsolete platforms use them. In ide-commons also got rid of LayoutBridgeWrapper and moved the code in LayoutLibrary which does not expose the bridge anymore, and instead expose an API similar to the LayoutBridge class. Updated ADT to use LayoutLibrary directly instead of going through LayoutLibrary.getBridge(). This allows us to hide some things like querying the API level and relying instead on Capabilities (with special handle for legacy bridges). Also added an error message to LayoutLibrary to display why it may have failed to load. Added a check to the API level and don't load layoutlib that are more recent than the client. Change-Id: Ie4e615d8d32485ee577bb88e95cd3f562bf590cb
Diffstat (limited to 'ide_common/src/com/android/ide')
-rw-r--r--ide_common/src/com/android/ide/common/layoutlib/DensityBasedResourceValue.java34
-rw-r--r--ide_common/src/com/android/ide/common/layoutlib/LayoutBridgeWrapper.java223
-rw-r--r--ide_common/src/com/android/ide/common/layoutlib/LayoutLibrary.java353
-rw-r--r--ide_common/src/com/android/ide/common/layoutlib/ResourceValue.java63
-rw-r--r--ide_common/src/com/android/ide/common/layoutlib/StyleResourceValue.java60
-rw-r--r--ide_common/src/com/android/ide/common/layoutlib/ValueResourceParser.java47
6 files changed, 362 insertions, 418 deletions
diff --git a/ide_common/src/com/android/ide/common/layoutlib/DensityBasedResourceValue.java b/ide_common/src/com/android/ide/common/layoutlib/DensityBasedResourceValue.java
deleted file mode 100644
index e1c0caa..0000000
--- a/ide_common/src/com/android/ide/common/layoutlib/DensityBasedResourceValue.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
- *
- * 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.common.layoutlib;
-
-import com.android.layoutlib.api.IDensityBasedResourceValue;
-
-public class DensityBasedResourceValue extends ResourceValue implements IDensityBasedResourceValue {
-
- private Density mDensity;
-
- public DensityBasedResourceValue(String type, String name, String value, Density density,
- boolean isFramework) {
- super(type, name, value, isFramework);
- mDensity = density;
- }
-
- public Density getDensity() {
- return mDensity;
- }
-}
diff --git a/ide_common/src/com/android/ide/common/layoutlib/LayoutBridgeWrapper.java b/ide_common/src/com/android/ide/common/layoutlib/LayoutBridgeWrapper.java
deleted file mode 100644
index 1d4ff62..0000000
--- a/ide_common/src/com/android/ide/common/layoutlib/LayoutBridgeWrapper.java
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
- *
- * 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.common.layoutlib;
-
-import com.android.layoutlib.api.ILayoutBridge;
-import com.android.layoutlib.api.ILayoutLog;
-import com.android.layoutlib.api.ILayoutResult;
-import com.android.layoutlib.api.LayoutBridge;
-import com.android.layoutlib.api.LayoutLog;
-import com.android.layoutlib.api.LayoutScene;
-import com.android.layoutlib.api.SceneParams;
-import com.android.layoutlib.api.SceneResult;
-import com.android.layoutlib.api.ViewInfo;
-import com.android.layoutlib.api.ILayoutResult.ILayoutViewInfo;
-import com.android.layoutlib.api.SceneParams.RenderingMode;
-import com.android.layoutlib.api.SceneResult.SceneStatus;
-
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Map;
-
-/**
- * {@link LayoutBridge} wrapper around a {@link ILayoutBridge}.
- * <p/>
- * The goal is to let tools only uses the latest API by providing a conversion interface
- * between the really old API ({@link ILayoutBridge}) and the new one ({@link ILayoutBridge}).
- *
- */
-@SuppressWarnings("deprecation")
-class LayoutBridgeWrapper extends LayoutBridge {
-
- private final ILayoutBridge mBridge;
- private final ClassLoader mClassLoader;
-
- LayoutBridgeWrapper(ILayoutBridge bridge, ClassLoader classLoader) {
- mBridge = bridge;
- mClassLoader = classLoader;
- }
-
- @Override
- public int getApiLevel() {
- int apiLevel = 1;
- try {
- apiLevel = mBridge.getApiLevel();
- } catch (AbstractMethodError e) {
- // the first version of the api did not have this method
- // so this is 1
- }
-
- return apiLevel;
- }
-
- @Override
- public boolean init(String fontOsLocation, Map<String, Map<String, Integer>> enumValueMap) {
- return mBridge.init(fontOsLocation, enumValueMap);
- }
-
- @Override
- public boolean dispose() {
- // there's no dispose in ILayoutBridge
- return true;
- }
-
-
- @Override
- public LayoutScene createScene(SceneParams params) {
- int apiLevel = mBridge.getApiLevel();
-
- // create a log wrapper since the older api requires a ILayoutLog
- final LayoutLog log = params.getLog();
- ILayoutLog logWrapper = new ILayoutLog() {
-
- public void warning(String message) {
- log.warning(null, message);
- }
-
- public void error(Throwable t) {
- log.error(null, t);
- }
-
- public void error(String message) {
- log.error(null, message);
- }
- };
-
- ILayoutResult result = null;
-
- if (apiLevel == 4) {
- // Final ILayoutBridge API added support for "render full height"
- result = mBridge.computeLayout(
- params.getLayoutDescription(), params.getProjectKey(),
- params.getScreenWidth(), params.getScreenHeight(),
- params.getRenderingMode() == RenderingMode.FULL_EXPAND ? true : false,
- params.getDensity(), params.getXdpi(), params.getYdpi(),
- params.getThemeName(), params.getIsProjectTheme(),
- params.getProjectResources(), params.getFrameworkResources(),
- params.getProjectCallback(), logWrapper);
- } else if (apiLevel == 3) {
- // api 3 add density support.
- result = mBridge.computeLayout(
- params.getLayoutDescription(), params.getProjectKey(),
- params.getScreenWidth(), params.getScreenHeight(),
- params.getDensity(), params.getXdpi(), params.getYdpi(),
- params.getThemeName(), params.getIsProjectTheme(),
- params.getProjectResources(), params.getFrameworkResources(),
- params.getProjectCallback(), logWrapper);
- } else if (apiLevel == 2) {
- // api 2 added boolean for separation of project/framework theme
- result = mBridge.computeLayout(
- params.getLayoutDescription(), params.getProjectKey(),
- params.getScreenWidth(), params.getScreenHeight(),
- params.getThemeName(), params.getIsProjectTheme(),
- params.getProjectResources(), params.getFrameworkResources(),
- params.getProjectCallback(), logWrapper);
- } else {
- // First api with no density/dpi, and project theme boolean mixed
- // into the theme name.
-
- // change the string if it's a custom theme to make sure we can
- // differentiate them
- String themeName = params.getThemeName();
- if (params.getIsProjectTheme()) {
- themeName = "*" + themeName; //$NON-NLS-1$
- }
-
- result = mBridge.computeLayout(
- params.getLayoutDescription(), params.getProjectKey(),
- params.getScreenWidth(), params.getScreenHeight(),
- themeName,
- params.getProjectResources(), params.getFrameworkResources(),
- params.getProjectCallback(), logWrapper);
- }
-
- // clean up that is not done by the ILayoutBridge itself
- cleanUp();
-
- return convertToScene(result);
- }
-
-
- @Override
- public void clearCaches(Object projectKey) {
- mBridge.clearCaches(projectKey);
- }
-
- /**
- * Converts a {@link ILayoutResult} to a {@link LayoutScene}.
- */
- private LayoutScene convertToScene(ILayoutResult result) {
-
- SceneResult sceneResult;
- ViewInfo rootViewInfo;
-
- if (result.getSuccess() == ILayoutResult.SUCCESS) {
- sceneResult = SceneStatus.SUCCESS.createResult();
- rootViewInfo = convertToViewInfo(result.getRootView());
- } else {
- sceneResult = SceneStatus.ERROR_UNKNOWN.createResult(result.getErrorMessage());
- rootViewInfo = null;
- }
-
- // create a BasicLayoutScene. This will return the given values but return the default
- // implementation for all method.
- // ADT should gracefully handle the default implementations of LayoutScene
- return new BasicLayoutScene(sceneResult, rootViewInfo, result.getImage());
- }
-
- /**
- * Converts a {@link ILayoutViewInfo} (and its children) to a {@link ViewInfo}.
- */
- private ViewInfo convertToViewInfo(ILayoutViewInfo view) {
- // create the view info.
- ViewInfo viewInfo = new ViewInfo(view.getName(), view.getViewKey(),
- view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
-
- // then convert the children
- ILayoutViewInfo[] children = view.getChildren();
- if (children != null) {
- ArrayList<ViewInfo> convertedChildren = new ArrayList<ViewInfo>(children.length);
- for (ILayoutViewInfo child : children) {
- convertedChildren.add(convertToViewInfo(child));
- }
- viewInfo.setChildren(convertedChildren);
- }
-
- return viewInfo;
- }
-
- /**
- * Post rendering clean-up that must be done here because it's not done in any layoutlib using
- * {@link ILayoutBridge}.
- */
- private void cleanUp() {
- try {
- Class<?> looperClass = mClassLoader.loadClass("android.os.Looper"); //$NON-NLS-1$
- Field threadLocalField = looperClass.getField("sThreadLocal"); //$NON-NLS-1$
- if (threadLocalField != null) {
- threadLocalField.setAccessible(true);
- // get object. Field is static so no need to pass an object
- ThreadLocal<?> threadLocal = (ThreadLocal<?>) threadLocalField.get(null);
- if (threadLocal != null) {
- threadLocal.remove();
- }
- }
- } catch (Exception e) {
- // do nothing.
- }
- }
-}
diff --git a/ide_common/src/com/android/ide/common/layoutlib/LayoutLibrary.java b/ide_common/src/com/android/ide/common/layoutlib/LayoutLibrary.java
index 5ec7ae5..740953e 100644
--- a/ide_common/src/com/android/ide/common/layoutlib/LayoutLibrary.java
+++ b/ide_common/src/com/android/ide/common/layoutlib/LayoutLibrary.java
@@ -18,17 +18,39 @@ package com.android.ide.common.layoutlib;
import com.android.ide.common.log.ILogger;
import com.android.ide.common.sdk.LoadStatus;
+import com.android.layoutlib.api.Capability;
import com.android.layoutlib.api.ILayoutBridge;
+import com.android.layoutlib.api.ILayoutLog;
+import com.android.layoutlib.api.ILayoutResult;
+import com.android.layoutlib.api.IResourceValue;
import com.android.layoutlib.api.LayoutBridge;
+import com.android.layoutlib.api.LayoutLog;
+import com.android.layoutlib.api.LayoutScene;
+import com.android.layoutlib.api.SceneParams;
+import com.android.layoutlib.api.SceneResult;
+import com.android.layoutlib.api.ViewInfo;
+import com.android.layoutlib.api.ILayoutResult.ILayoutViewInfo;
+import com.android.layoutlib.api.SceneParams.RenderingMode;
+import com.android.layoutlib.api.SceneResult.SceneStatus;
import java.io.File;
import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Map;
/**
- * Class representing and allowing to load the layoutlib jar file.
+ * Class to use the Layout library.
+ * <p/>
+ * Use {@link #load(String, ILogger)} to load the jar file.
+ * <p/>
+ * Use the layout library with:
+ * {@link #init(String, Map)}, {@link #supports(Capability)}, {@link #createScene(SceneParams)},
+ * {@link #dispose()}, {@link #clearCaches(Object)}.
+ *
*/
@SuppressWarnings("deprecation")
public class LayoutLibrary {
@@ -37,25 +59,31 @@ public class LayoutLibrary {
/** Link to the layout bridge */
private final LayoutBridge mBridge;
+ /** Link to a ILayoutBridge in case loaded an older library */
+ private final ILayoutBridge mLegacyBridge;
/** Status of the layoutlib.jar loading */
private final LoadStatus mStatus;
+ /** Message associated with the {@link LoadStatus}. This is mostly used when
+ * {@link #getStatus()} returns {@link LoadStatus#FAILED}.
+ */
+ private final String mLoadMessage;
/** classloader used to load the jar file */
private final ClassLoader mClassLoader;
/**
- * Returns the loaded {@link LayoutBridge} object or null if the loading failed.
- */
- public LayoutBridge getBridge() {
- return mBridge;
- }
-
- /**
* Returns the {@link LoadStatus} of the loading of the layoutlib jar file.
*/
public LoadStatus getStatus() {
return mStatus;
}
+ /** Returns the message associated with the {@link LoadStatus}. This is mostly used when
+ * {@link #getStatus()} returns {@link LoadStatus#FAILED}.
+ */
+ public String getLoadMessage() {
+ return mLoadMessage;
+ }
+
/**
* Returns the classloader used to load the classes in the layoutlib jar file.
*/
@@ -77,7 +105,9 @@ public class LayoutLibrary {
public static LayoutLibrary load(String layoutLibJarOsPath, ILogger log) {
LoadStatus status = LoadStatus.LOADING;
+ String message = null;
LayoutBridge bridge = null;
+ ILayoutBridge legacyBridge = null;
ClassLoader classLoader = null;
try {
@@ -108,36 +138,327 @@ public class LayoutLibrary {
if (bridgeObject instanceof LayoutBridge) {
bridge = (LayoutBridge)bridgeObject;
} else if (bridgeObject instanceof ILayoutBridge) {
- bridge = new LayoutBridgeWrapper((ILayoutBridge) bridgeObject,
- classLoader);
+ legacyBridge = (ILayoutBridge) bridgeObject;
}
}
}
- if (bridge == null) {
+ if (bridge == null && legacyBridge == null) {
status = LoadStatus.FAILED;
+ message = "Failed to load " + CLASS_BRIDGE; //$NON-NLS-1$
if (log != null) {
- log.error(null, "Failed to load " + CLASS_BRIDGE); //$NON-NLS-1$
+ log.error(null,
+ "Failed to load " + //$NON-NLS-1$
+ CLASS_BRIDGE +
+ " from " + //$NON-NLS-1$
+ layoutLibJarOsPath);
}
} else {
- // mark the lib as loaded.
+ // mark the lib as loaded, unless it's overridden below.
status = LoadStatus.LOADED;
+
+ // check the API, only if it's not a legacy bridge
+ if (bridge != null) {
+ int api = bridge.getApiLevel();
+ if (api > LayoutBridge.API_CURRENT) {
+ status = LoadStatus.FAILED;
+ message = "LayoutLib is too recent. Update your tool!";
+ }
+ }
}
}
} catch (Throwable t) {
status = LoadStatus.FAILED;
+ Throwable cause = t;
+ while (cause.getCause() != null) {
+ cause = cause.getCause();
+ }
+ message = "Failed to load the LayoutLib: " + cause.getMessage();
// log the error.
if (log != null) {
- log.error(t, "Failed to load the LayoutLib");
+ log.error(t, message);
+ }
+ }
+
+ return new LayoutLibrary(bridge, legacyBridge, classLoader, status, message);
+ }
+
+ // ------ Layout Lib API proxy
+
+ /**
+ * Returns whether the LayoutLibrary supports a given {@link Capability}.
+ * @return true if it supports it.
+ *
+ * @see LayoutBridge#getCapabilities()
+ *
+ */
+ public boolean supports(Capability capability) {
+ if (mBridge != null) {
+ return mBridge.getCapabilities().contains(capability);
+ }
+
+ if (mLegacyBridge != null) {
+ switch (capability) {
+ case UNBOUND_RENDERING:
+ // legacy stops at 4. 5 is new API.
+ return getLegacyApiLevel() == 4;
}
}
- return new LayoutLibrary(bridge, classLoader, status);
+ return false;
}
- private LayoutLibrary(LayoutBridge bridge, ClassLoader classLoader, LoadStatus status) {
+ /**
+ * Initializes the Layout Library object. This must be called before any other action is taken
+ * on the instance.
+ *
+ * @param fontOsLocation the location of the fonts in the SDK target.
+ * @param enumValueMap map attrName => { map enumFlagName => Integer value }. This is typically
+ * read from attrs.xml in the SDK target.
+ * @return true if success.
+ *
+ * @see LayoutBridge#init(String, Map)
+ */
+ public boolean init(String fontOsLocation, Map<String, Map<String, Integer>> enumValueMap) {
+ if (mBridge != null) {
+ return mBridge.init(fontOsLocation, enumValueMap);
+ } else if (mLegacyBridge != null) {
+ return mLegacyBridge.init(fontOsLocation, enumValueMap);
+ }
+
+ return false;
+ }
+
+ /**
+ * Prepares the layoutlib to unloaded.
+ *
+ * @see LayoutBridge#dispose()
+ */
+ public boolean dispose() {
+ if (mBridge != null) {
+ return mBridge.dispose();
+ }
+
+ return true;
+ }
+
+ /**
+ * Starts a layout session by inflating and rendering it. The method returns a
+ * {@link LayoutScene} on which further actions can be taken.
+ * <p/>
+ * Before taking further actions on the scene, it is recommended to use
+ * {@link #supports(Capability)} to check what the scene can do.
+ *
+ * @return a new {@link ILayoutScene} object that contains the result of the scene creation and
+ * first rendering or null if {@link #getStatus()} doesn't return {@link LoadStatus#LOADED}.
+ *
+ * @see LayoutBridge#createScene(SceneParams)
+ */
+ public LayoutScene createScene(SceneParams params) {
+ if (mBridge != null) {
+ return mBridge.createScene(params);
+ } else if (mLegacyBridge != null) {
+ return createLegacyScene(params);
+ }
+
+ return null;
+ }
+
+ /**
+ * Clears the resource cache for a specific project.
+ * <p/>This cache contains bitmaps and nine patches that are loaded from the disk and reused
+ * until this method is called.
+ * <p/>The cache is not configuration dependent and should only be cleared when a
+ * resource changes (at this time only bitmaps and 9 patches go into the cache).
+ *
+ * @param projectKey the key for the project.
+ *
+ * @see LayoutBridge#clearCaches(Object)
+ */
+ public void clearCaches(Object projectKey) {
+ if (mBridge != null) {
+ mBridge.clearCaches(projectKey);
+ } else if (mLegacyBridge != null) {
+ mLegacyBridge.clearCaches(projectKey);
+ }
+ }
+
+ // ------ Implementation
+
+ private LayoutLibrary(LayoutBridge bridge, ILayoutBridge legacyBridge, ClassLoader classLoader,
+ LoadStatus status, String message) {
mBridge = bridge;
+ mLegacyBridge = legacyBridge;
mClassLoader = classLoader;
mStatus = status;
+ mLoadMessage = message;
+ }
+
+ /**
+ * Returns the API level of the legacy bridge.
+ * <p/>
+ * This handles the case where ILayoutBridge does not have a {@link ILayoutBridge#getApiLevel()}
+ * (at API level 1).
+ * <p/>
+ * {@link ILayoutBridge#getApiLevel()} should never called directly.
+ *
+ * @return the api level of {@link #mLegacyBridge}.
+ */
+ private int getLegacyApiLevel() {
+ int apiLevel = 1;
+ try {
+ apiLevel = mLegacyBridge.getApiLevel();
+ } catch (AbstractMethodError e) {
+ // the first version of the api did not have this method
+ // so this is 1
+ }
+
+ return apiLevel;
+ }
+
+ private LayoutScene createLegacyScene(SceneParams params) {
+ int apiLevel = mLegacyBridge.getApiLevel();
+
+ // create a log wrapper since the older api requires a ILayoutLog
+ final LayoutLog log = params.getLog();
+ ILayoutLog logWrapper = new ILayoutLog() {
+
+ public void warning(String message) {
+ log.warning(null, message);
+ }
+
+ public void error(Throwable t) {
+ log.error(null, t);
+ }
+
+ public void error(String message) {
+ log.error(null, message);
+ }
+ };
+
+ // convert the map of ResourceValue into IResourceValue. Super ugly but works.
+ @SuppressWarnings("unchecked")
+ Map<String, Map<String, IResourceValue>> projectMap =
+ (Map<String, Map<String, IResourceValue>>)(Map) params.getProjectResources();
+ @SuppressWarnings("unchecked")
+ Map<String, Map<String, IResourceValue>> frameworkMap =
+ (Map<String, Map<String, IResourceValue>>)(Map) params.getFrameworkResources();
+
+ ILayoutResult result = null;
+
+ if (apiLevel == 4) {
+ // Final ILayoutBridge API added support for "render full height"
+ result = mLegacyBridge.computeLayout(
+ params.getLayoutDescription(), params.getProjectKey(),
+ params.getScreenWidth(), params.getScreenHeight(),
+ params.getRenderingMode() == RenderingMode.FULL_EXPAND ? true : false,
+ params.getDensity(), params.getXdpi(), params.getYdpi(),
+ params.getThemeName(), params.getIsProjectTheme(),
+ projectMap, frameworkMap,
+ params.getProjectCallback(), logWrapper);
+ } else if (apiLevel == 3) {
+ // api 3 add density support.
+ result = mLegacyBridge.computeLayout(
+ params.getLayoutDescription(), params.getProjectKey(),
+ params.getScreenWidth(), params.getScreenHeight(),
+ params.getDensity(), params.getXdpi(), params.getYdpi(),
+ params.getThemeName(), params.getIsProjectTheme(),
+ projectMap, frameworkMap,
+ params.getProjectCallback(), logWrapper);
+ } else if (apiLevel == 2) {
+ // api 2 added boolean for separation of project/framework theme
+ result = mLegacyBridge.computeLayout(
+ params.getLayoutDescription(), params.getProjectKey(),
+ params.getScreenWidth(), params.getScreenHeight(),
+ params.getThemeName(), params.getIsProjectTheme(),
+ projectMap, frameworkMap,
+ params.getProjectCallback(), logWrapper);
+ } else {
+ // First api with no density/dpi, and project theme boolean mixed
+ // into the theme name.
+
+ // change the string if it's a custom theme to make sure we can
+ // differentiate them
+ String themeName = params.getThemeName();
+ if (params.getIsProjectTheme()) {
+ themeName = "*" + themeName; //$NON-NLS-1$
+ }
+
+ result = mLegacyBridge.computeLayout(
+ params.getLayoutDescription(), params.getProjectKey(),
+ params.getScreenWidth(), params.getScreenHeight(),
+ themeName,
+ projectMap, frameworkMap,
+ params.getProjectCallback(), logWrapper);
+ }
+
+ // clean up that is not done by the ILayoutBridge itself
+ legacyCleanUp();
+
+ return convertToScene(result);
+ }
+
+ /**
+ * Converts a {@link ILayoutResult} to a {@link LayoutScene}.
+ */
+ private LayoutScene convertToScene(ILayoutResult result) {
+
+ SceneResult sceneResult;
+ ViewInfo rootViewInfo;
+
+ if (result.getSuccess() == ILayoutResult.SUCCESS) {
+ sceneResult = SceneStatus.SUCCESS.createResult();
+ rootViewInfo = convertToViewInfo(result.getRootView());
+ } else {
+ sceneResult = SceneStatus.ERROR_UNKNOWN.createResult(result.getErrorMessage());
+ rootViewInfo = null;
+ }
+
+ // create a BasicLayoutScene. This will return the given values but return the default
+ // implementation for all method.
+ // ADT should gracefully handle the default implementations of LayoutScene
+ return new BasicLayoutScene(sceneResult, rootViewInfo, result.getImage());
+ }
+
+ /**
+ * Converts a {@link ILayoutViewInfo} (and its children) to a {@link ViewInfo}.
+ */
+ private ViewInfo convertToViewInfo(ILayoutViewInfo view) {
+ // create the view info.
+ ViewInfo viewInfo = new ViewInfo(view.getName(), view.getViewKey(),
+ view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
+
+ // then convert the children
+ ILayoutViewInfo[] children = view.getChildren();
+ if (children != null) {
+ ArrayList<ViewInfo> convertedChildren = new ArrayList<ViewInfo>(children.length);
+ for (ILayoutViewInfo child : children) {
+ convertedChildren.add(convertToViewInfo(child));
+ }
+ viewInfo.setChildren(convertedChildren);
+ }
+
+ return viewInfo;
+ }
+
+ /**
+ * Post rendering clean-up that must be done here because it's not done in any layoutlib using
+ * {@link ILayoutBridge}.
+ */
+ private void legacyCleanUp() {
+ try {
+ Class<?> looperClass = mClassLoader.loadClass("android.os.Looper"); //$NON-NLS-1$
+ Field threadLocalField = looperClass.getField("sThreadLocal"); //$NON-NLS-1$
+ if (threadLocalField != null) {
+ threadLocalField.setAccessible(true);
+ // get object. Field is static so no need to pass an object
+ ThreadLocal<?> threadLocal = (ThreadLocal<?>) threadLocalField.get(null);
+ if (threadLocal != null) {
+ threadLocal.remove();
+ }
+ }
+ } catch (Exception e) {
+ // do nothing.
+ }
}
}
diff --git a/ide_common/src/com/android/ide/common/layoutlib/ResourceValue.java b/ide_common/src/com/android/ide/common/layoutlib/ResourceValue.java
deleted file mode 100644
index 382d961..0000000
--- a/ide_common/src/com/android/ide/common/layoutlib/ResourceValue.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
- *
- * 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.common.layoutlib;
-
-import com.android.layoutlib.api.IResourceValue;
-
-public class ResourceValue implements IResourceValue {
- private final String mType;
- private final String mName;
- private String mValue = null;
- private final boolean mIsFramwork;
-
- public ResourceValue(String type, String name, boolean isFramwork) {
- mType = type;
- mName = name;
- mIsFramwork = isFramwork;
- }
-
- public ResourceValue(String type, String name, String value, boolean isFramework) {
- mType = type;
- mName = name;
- mValue = value;
- mIsFramwork = isFramework;
- }
-
- public String getType() {
- return mType;
- }
-
- public final String getName() {
- return mName;
- }
-
- public final String getValue() {
- return mValue;
- }
-
- public final void setValue(String value) {
- mValue = value;
- }
-
- public void replaceWith(ResourceValue value) {
- mValue = value.mValue;
- }
-
- public boolean isFramework() {
- return mIsFramwork;
- }
-}
diff --git a/ide_common/src/com/android/ide/common/layoutlib/StyleResourceValue.java b/ide_common/src/com/android/ide/common/layoutlib/StyleResourceValue.java
deleted file mode 100644
index 721b16c..0000000
--- a/ide_common/src/com/android/ide/common/layoutlib/StyleResourceValue.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
- *
- * 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.common.layoutlib;
-
-import com.android.layoutlib.api.IResourceValue;
-import com.android.layoutlib.api.IStyleResourceValue;
-
-import java.util.HashMap;
-
-public final class StyleResourceValue extends ResourceValue implements IStyleResourceValue {
-
- private String mParentStyle = null;
- private HashMap<String, IResourceValue> mItems = new HashMap<String, IResourceValue>();
-
- public StyleResourceValue(String type, String name, boolean isFramework) {
- super(type, name, isFramework);
- }
-
- public StyleResourceValue(String type, String name, String parentStyle, boolean isFramework) {
- super(type, name, isFramework);
- mParentStyle = parentStyle;
- }
-
- public String getParentStyle() {
- return mParentStyle;
- }
-
- public IResourceValue findItem(String name) {
- return mItems.get(name);
- }
-
- public void addItem(IResourceValue value) {
- mItems.put(value.getName(), value);
- }
-
- @Override
- public void replaceWith(ResourceValue value) {
- super.replaceWith(value);
-
- if (value instanceof StyleResourceValue) {
- mItems.clear();
- mItems.putAll(((StyleResourceValue)value).mItems);
- }
- }
-
-}
diff --git a/ide_common/src/com/android/ide/common/layoutlib/ValueResourceParser.java b/ide_common/src/com/android/ide/common/layoutlib/ValueResourceParser.java
index c784f1f..cc65b9d 100644
--- a/ide_common/src/com/android/ide/common/layoutlib/ValueResourceParser.java
+++ b/ide_common/src/com/android/ide/common/layoutlib/ValueResourceParser.java
@@ -16,6 +16,9 @@
package com.android.ide.common.layoutlib;
+import com.android.layoutlib.api.ResourceValue;
+import com.android.layoutlib.api.StyleResourceValue;
+
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
@@ -31,25 +34,25 @@ public final class ValueResourceParser extends DefaultHandler {
private final static String ATTR_NAME = "name";
private final static String ATTR_TYPE = "type";
private final static String ATTR_PARENT = "parent";
-
+
// Resource type definition
private final static String RES_STYLE = "style";
private final static String RES_ATTR = "attr";
-
+
private final static String DEFAULT_NS_PREFIX = "android:";
private final static int DEFAULT_NS_PREFIX_LEN = DEFAULT_NS_PREFIX.length();
-
+
public interface IValueResourceRepository {
void addResourceValue(String resType, ResourceValue value);
}
-
+
private boolean inResources = false;
private int mDepth = 0;
private StyleResourceValue mCurrentStyle = null;
private ResourceValue mCurrentValue = null;
private IValueResourceRepository mRepository;
private final boolean mIsFramework;
-
+
public ValueResourceParser(IValueResourceRepository repository, boolean isFramework) {
mRepository = repository;
mIsFramework = isFramework;
@@ -60,7 +63,7 @@ public final class ValueResourceParser extends DefaultHandler {
if (mCurrentValue != null) {
mCurrentValue.setValue(trimXmlWhitespaces(mCurrentValue.getValue()));
}
-
+
if (inResources && qName.equals(NODE_RESOURCES)) {
inResources = false;
} else if (mDepth == 2) {
@@ -69,7 +72,7 @@ public final class ValueResourceParser extends DefaultHandler {
} else if (mDepth == 3) {
mCurrentValue = null;
}
-
+
mDepth--;
super.endElement(uri, localName, qName);
}
@@ -85,7 +88,7 @@ public final class ValueResourceParser extends DefaultHandler {
}
} else if (mDepth == 2 && inResources == true) {
String type;
-
+
// if the node is <item>, we get the type from the attribute "type"
if (NODE_ITEM.equals(qName)) {
type = attributes.getValue(ATTR_TYPE);
@@ -118,16 +121,16 @@ public final class ValueResourceParser extends DefaultHandler {
if (name.startsWith(DEFAULT_NS_PREFIX)) {
name = name.substring(DEFAULT_NS_PREFIX_LEN);
}
-
+
mCurrentValue = new ResourceValue(null, name, mIsFramework);
- mCurrentStyle.addItem(mCurrentValue);
+ mCurrentStyle.addValue(mCurrentValue);
}
}
} finally {
super.startElement(uri, localName, qName, attributes);
}
}
-
+
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
if (mCurrentValue != null) {
@@ -139,7 +142,7 @@ public final class ValueResourceParser extends DefaultHandler {
}
}
}
-
+
public static String trimXmlWhitespaces(String value) {
if (value == null) {
return null;
@@ -147,7 +150,7 @@ public final class ValueResourceParser extends DefaultHandler {
// look for carriage return and replace all whitespace around it by just 1 space.
int index;
-
+
while ((index = value.indexOf('\n')) != -1) {
// look for whitespace on each side
int left = index - 1;
@@ -158,7 +161,7 @@ public final class ValueResourceParser extends DefaultHandler {
break;
}
}
-
+
int right = index + 1;
int count = value.length();
while (right < count) {
@@ -168,7 +171,7 @@ public final class ValueResourceParser extends DefaultHandler {
break;
}
}
-
+
// remove all between left and right (non inclusive) and replace by a single space.
String leftString = null;
if (left >= 0) {
@@ -178,7 +181,7 @@ public final class ValueResourceParser extends DefaultHandler {
if (right < count) {
rightString = value.substring(right);
}
-
+
if (leftString != null) {
value = leftString;
if (rightString != null) {
@@ -188,21 +191,21 @@ public final class ValueResourceParser extends DefaultHandler {
value = rightString != null ? rightString : "";
}
}
-
+
// now we un-escape the string
int length = value.length();
char[] buffer = value.toCharArray();
-
+
for (int i = 0 ; i < length ; i++) {
if (buffer[i] == '\\' && i + 1 < length) {
if (buffer[i+1] == 'u') {
if (i + 5 < length) {
// this is unicode char \u1234
int unicodeChar = Integer.parseInt(new String(buffer, i+2, 4), 16);
-
+
// put the unicode char at the location of the \
buffer[i] = (char)unicodeChar;
-
+
// offset the rest of the buffer since we go from 6 to 1 char
if (i + 6 < buffer.length) {
System.arraycopy(buffer, i+6, buffer, i+1, length - i - 6);
@@ -214,14 +217,14 @@ public final class ValueResourceParser extends DefaultHandler {
// replace the 'n' char with \n
buffer[i+1] = '\n';
}
-
+
// offset the buffer to erase the \
System.arraycopy(buffer, i+1, buffer, i, length - i - 1);
length--;
}
}
}
-
+
return new String(buffer, 0, length);
}
}