/* * 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.ILayoutResult; import com.android.layoutlib.api.LayoutBridge; 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 java.lang.reflect.Field; import java.util.ArrayList; import java.util.Map; /** * {@link LayoutBridge} wrapper around a {@link ILayoutBridge}. *

* 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> 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(); 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.getRenderFullSize(), params.getDensity(), params.getXdpi(), params.getYdpi(), params.getThemeName(), params.getIsProjectTheme(), params.getProjectResources(), params.getFrameworkResources(), params.getProjectCallback(), params.getLogger()); } 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(), params.getLogger()); } 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(), params.getLogger()); } 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(), params.getLogger()); } // 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 = SceneResult.SUCCESS; rootViewInfo = convertToViewInfo(result.getRootView()); } else { sceneResult = new SceneResult(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 convertedChildren = new ArrayList(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. } } }