RenderSessionImpl.java revision 2d240967c9bc38cbf69967457b33f953f8826e96
13bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet/*
23bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet * Copyright (C) 2010 The Android Open Source Project
33bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet *
43bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet * Licensed under the Apache License, Version 2.0 (the "License");
53bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet * you may not use this file except in compliance with the License.
63bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet * You may obtain a copy of the License at
73bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet *
83bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet *      http://www.apache.org/licenses/LICENSE-2.0
93bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet *
103bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet * Unless required by applicable law or agreed to in writing, software
113bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet * distributed under the License is distributed on an "AS IS" BASIS,
123bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet * See the License for the specific language governing permissions and
143bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet * limitations under the License.
153bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet */
163bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
173bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetpackage com.android.layoutlib.bridge.impl;
183bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
193bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport com.android.internal.util.XmlUtils;
203bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport com.android.layoutlib.api.IProjectCallback;
213bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport com.android.layoutlib.api.IResourceValue;
223bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport com.android.layoutlib.api.IStyleResourceValue;
233bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport com.android.layoutlib.api.LayoutBridge;
243bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport com.android.layoutlib.api.SceneParams;
253bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport com.android.layoutlib.api.SceneResult;
263bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport com.android.layoutlib.api.ViewInfo;
273bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport com.android.layoutlib.api.IDensityBasedResourceValue.Density;
283bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport com.android.layoutlib.bridge.BridgeConstants;
293bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport com.android.layoutlib.bridge.android.BridgeContext;
303bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport com.android.layoutlib.bridge.android.BridgeInflater;
313bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport com.android.layoutlib.bridge.android.BridgeWindow;
323bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport com.android.layoutlib.bridge.android.BridgeWindowSession;
333bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
343bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
353bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.app.Fragment_Delegate;
363bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.graphics.Bitmap;
373bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.graphics.Bitmap_Delegate;
383bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.graphics.Canvas;
393bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.graphics.Canvas_Delegate;
403bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.graphics.drawable.Drawable;
413bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.os.Handler;
423bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.os.Looper;
433bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.util.DisplayMetrics;
443bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.util.TypedValue;
453bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.view.View;
463bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.view.ViewGroup;
473bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.view.View.AttachInfo;
483bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.view.View.MeasureSpec;
493bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.widget.FrameLayout;
503bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.widget.TabHost;
513bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.widget.TabWidget;
523bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
533bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport java.awt.Color;
543bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport java.awt.Graphics2D;
553bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport java.awt.image.BufferedImage;
563bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport java.util.ArrayList;
573bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport java.util.Collection;
583bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport java.util.HashMap;
593bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport java.util.List;
603bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport java.util.Map;
613bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
623bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet/**
633bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet * Class managing a layout "scene".
643bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet *
653bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet * A scene is a stateful representation of a layout file. It is initialized with data coming through
663bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet * the {@link LayoutBridge} API to inflate the layout. Further actions and rendering can then
673bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet * be done on the layout.
683bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet *
693bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet */
703bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetpublic class LayoutSceneImpl {
713bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
723bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private static final int DEFAULT_TITLE_BAR_HEIGHT = 25;
733bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private static final int DEFAULT_STATUS_BAR_HEIGHT = 25;
743bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
753bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private final SceneParams mParams;
763bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
773bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    // scene state
783bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private BridgeContext mContext;
793bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private BridgeXmlBlockParser mBlockParser;
803bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private BridgeInflater mInflater;
813bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private IStyleResourceValue mCurrentTheme;
823bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private int mScreenOffset;
833bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private IResourceValue mWindowBackground;
843bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private FrameLayout mViewRoot;
853bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
863bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    // information being returned through the API
873bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private BufferedImage mImage;
883bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private ViewInfo mViewInfo;
893bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
903bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private static final class PostInflateException extends Exception {
913bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        private static final long serialVersionUID = 1L;
923bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
933bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        public PostInflateException(String message) {
943bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            super(message);
953bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
963bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
973bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
983bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    /**
993bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * Creates a layout scene with all the information coming from the layout bridge API.
1003bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     *
1013bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * This also calls {@link LayoutSceneImpl#prepare()}.
1023bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * <p>
1033bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * <b>THIS MUST BE INSIDE A SYNCHRONIZED BLOCK on the BRIDGE OBJECT.<b>
1043bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     *
1053bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @see LayoutBridge#createScene(com.android.layoutlib.api.SceneParams)
1063bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     */
1073bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    public LayoutSceneImpl(SceneParams params) {
1083bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // we need to make sure the Looper has been initialized for this thread.
1093bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // this is required for View that creates Handler objects.
1103bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (Looper.myLooper() == null) {
1113bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            Looper.prepare();
1123bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
1133bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
1143bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // copy the params.
1153bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        mParams = new SceneParams(params);
1163bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
1173bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // setup the display Metrics.
1183bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        DisplayMetrics metrics = new DisplayMetrics();
1193bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        metrics.densityDpi = mParams.getDensity();
1203bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        metrics.density = mParams.getDensity() / (float) DisplayMetrics.DENSITY_DEFAULT;
1213bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        metrics.scaledDensity = metrics.density;
1223bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        metrics.widthPixels = mParams.getScreenWidth();
1233bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        metrics.heightPixels = mParams.getScreenHeight();
1243bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        metrics.xdpi = mParams.getXdpi();
1253bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        metrics.ydpi = mParams.getYdpi();
1263bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
1273bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // find the current theme and compute the style inheritance map
1283bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        Map<IStyleResourceValue, IStyleResourceValue> styleParentMap =
1293bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            new HashMap<IStyleResourceValue, IStyleResourceValue>();
1303bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
1313bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        mCurrentTheme = computeStyleMaps(mParams.getThemeName(), mParams.getIsProjectTheme(),
1323bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                mParams.getProjectResources().get(BridgeConstants.RES_STYLE),
1333bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                mParams.getFrameworkResources().get(BridgeConstants.RES_STYLE), styleParentMap);
1343bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
1353bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // build the context
1363bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        mContext = new BridgeContext(mParams.getProjectKey(), metrics, mCurrentTheme,
1373bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                mParams.getProjectResources(), mParams.getFrameworkResources(),
1383bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                styleParentMap, mParams.getProjectCallback(), mParams.getLogger());
1393bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
1403bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // make sure the Resources object references the context (and other objects) for this
1413bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // scene
1423bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        mContext.initResources();
1433bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
1443bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // get the screen offset and window-background resource
1453bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        mWindowBackground = null;
1463bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        mScreenOffset = 0;
1473bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (mCurrentTheme != null && mParams.isCustomBackgroundEnabled() == false) {
1483bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            mWindowBackground = mContext.findItemInStyle(mCurrentTheme, "windowBackground");
1493bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            mWindowBackground = mContext.resolveResValue(mWindowBackground);
1503bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
1513bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            mScreenOffset = getScreenOffset(mParams.getFrameworkResources(), mCurrentTheme, mContext);
1523bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
1533bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
1543bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // build the inflater and parser.
1553bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        mInflater = new BridgeInflater(mContext, mParams.getProjectCallback());
1563bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        mContext.setBridgeInflater(mInflater);
1573bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        mInflater.setFactory2(mContext);
1583bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
1593bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        mBlockParser = new BridgeXmlBlockParser(mParams.getLayoutDescription(),
1603bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                mContext, false /* platformResourceFlag */);
1613bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
1623bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
1633bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    /**
1643bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * Prepares the scene for action.
1653bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * <p>
1663bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * <b>THIS MUST BE INSIDE A SYNCHRONIZED BLOCK on the BRIDGE OBJECT.<b>
1673bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     */
1683bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    public void prepare() {
1693bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // we need to make sure the Looper has been initialized for this thread.
1703bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // this is required for View that creates Handler objects.
1713bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (Looper.myLooper() == null) {
1723bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            Looper.prepare();
1733bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
1743bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
1753bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // make sure the Resources object references the context (and other objects) for this
1763bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // scene
1773bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        mContext.initResources();
1783bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
1793bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
1803bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    /**
1813bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * Cleans up the scene after an action.
1823bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * <p>
1833bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * <b>THIS MUST BE INSIDE A SYNCHRONIZED BLOCK on the BRIDGE OBJECT.<b>
1843bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     */
1853bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    public void cleanup() {
1863bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // clean up the looper
1873bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        Looper.sThreadLocal.remove();
1883bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
1893bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // Make sure to remove static references, otherwise we could not unload the lib
1903bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        mContext.disposeResources();
1913bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
1923bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
1933bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    /**
1943bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * Inflates the layout.
1953bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * <p>
1963bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * <b>THIS MUST BE INSIDE A SYNCHRONIZED BLOCK on the BRIDGE OBJECT.<b>
1973bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     */
1983bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    public SceneResult inflate() {
1993bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        try {
2003bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
2013bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            mViewRoot = new FrameLayout(mContext);
2023bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
2033bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // Sets the project callback (custom view loader) to the fragment delegate so that
2043bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // it can instantiate the custom Fragment.
2053bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            Fragment_Delegate.setProjectCallback(mParams.getProjectCallback());
2063bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
2073bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            View view = mInflater.inflate(mBlockParser, mViewRoot);
2083bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
2093bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // post-inflate process. For now this supports TabHost/TabWidget
2103bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            postInflateProcess(view, mParams.getProjectCallback());
2113bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
2123bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            Fragment_Delegate.setProjectCallback(null);
2133bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
2143bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // set the AttachInfo on the root view.
2153bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            AttachInfo info = new AttachInfo(new BridgeWindowSession(), new BridgeWindow(),
2163bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    new Handler(), null);
2173bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            info.mHasWindowFocus = true;
2183bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            info.mWindowVisibility = View.VISIBLE;
2193bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            info.mInTouchMode = false; // this is so that we can display selections.
2203bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            mViewRoot.dispatchAttachedToWindow(info, 0);
2213bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
2223bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // get the background drawable
2233bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            if (mWindowBackground != null) {
2243bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                Drawable d = ResourceHelper.getDrawable(mWindowBackground,
2253bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                        mContext, true /* isFramework */);
2263bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                mViewRoot.setBackgroundDrawable(d);
2273bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            }
2283bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
2293bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            return SceneResult.SUCCESS;
2303bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        } catch (PostInflateException e) {
2313bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            return new SceneResult("Error during post inflation process:\n" + e.getMessage());
2323bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        } catch (Throwable e) {
2333bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // get the real cause of the exception.
2343bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            Throwable t = e;
2353bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            while (t.getCause() != null) {
2363bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                t = t.getCause();
2373bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            }
2383bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
2393bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // log it
2403bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            mParams.getLogger().error(t);
2413bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
2423bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            return new SceneResult("Unknown error during inflation.", t);
2433bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
2443bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
2453bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
2463bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    /**
2473bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * Renders the scene.
2483bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * <p>
2493bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * <b>THIS MUST BE INSIDE A SYNCHRONIZED BLOCK on the BRIDGE OBJECT.<b>
2503bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     */
2513bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    public SceneResult render() {
2523bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        try {
2533bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            if (mViewRoot == null) {
2543bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                return new SceneResult("Layout has not been inflated!");
2553bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            }
2563bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // measure the views
2573bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            int w_spec, h_spec;
2583bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
2593bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            int renderScreenWidth = mParams.getScreenWidth();
2603bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            int renderScreenHeight = mParams.getScreenHeight();
2613bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
2623bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            if (mParams.getRenderFullSize()) {
2633bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                // measure the full size needed by the layout.
2643bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                w_spec = MeasureSpec.makeMeasureSpec(renderScreenWidth,
2653bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                        MeasureSpec.UNSPECIFIED); // this lets us know the actual needed size
2663bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                h_spec = MeasureSpec.makeMeasureSpec(renderScreenHeight - mScreenOffset,
2673bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                        MeasureSpec.UNSPECIFIED); // this lets us know the actual needed size
2683bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                mViewRoot.measure(w_spec, h_spec);
2693bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
2703bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                int neededWidth = mViewRoot.getChildAt(0).getMeasuredWidth();
2713bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                if (neededWidth > renderScreenWidth) {
2723bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    renderScreenWidth = neededWidth;
2733bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                }
2743bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
2753bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                int neededHeight = mViewRoot.getChildAt(0).getMeasuredHeight();
2763bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                if (neededHeight > renderScreenHeight - mScreenOffset) {
2773bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    renderScreenHeight = neededHeight + mScreenOffset;
2783bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                }
2793bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            }
2803bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
2813bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // remeasure with the size we need
2823bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // This must always be done before the call to layout
2833bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            w_spec = MeasureSpec.makeMeasureSpec(renderScreenWidth, MeasureSpec.EXACTLY);
2843bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            h_spec = MeasureSpec.makeMeasureSpec(renderScreenHeight - mScreenOffset,
2853bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    MeasureSpec.EXACTLY);
2863bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            mViewRoot.measure(w_spec, h_spec);
2873bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
2883bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // now do the layout.
2893bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            mViewRoot.layout(0, mScreenOffset, renderScreenWidth, renderScreenHeight);
2903bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
2913bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // draw the views
2923bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // create the BufferedImage into which the layout will be rendered.
2933bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            mImage = new BufferedImage(renderScreenWidth, renderScreenHeight - mScreenOffset,
2943bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    BufferedImage.TYPE_INT_ARGB);
2953bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
2963bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            if (mParams.isCustomBackgroundEnabled()) {
2973bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                Graphics2D gc = mImage.createGraphics();
2988396d6f3a1f8cb397a1ef54f0a38893d64d7c275Tor Norbye                gc.setColor(new Color(mParams.getCustomBackgroundColor(), true));
2993bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                gc.fillRect(0, 0, renderScreenWidth, renderScreenHeight - mScreenOffset);
3003bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                gc.dispose();
3013bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            }
3023bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
3033bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // create an Android bitmap around the BufferedImage
3043bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            Bitmap bitmap = Bitmap_Delegate.createBitmap(mImage,
3053bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    Density.getEnum(mParams.getDensity()));
3063bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
3073bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // create a Canvas around the Android bitmap
3083bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            Canvas canvas = new Canvas(bitmap);
3093bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
3103bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // to set the logger, get the native delegate
3113bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas);
3123bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            canvasDelegate.setLogger(mParams.getLogger());
3133bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
3143bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            mViewRoot.draw(canvas);
3153bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            canvasDelegate.dispose();
3163bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
3173bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            mViewInfo = visit(((ViewGroup)mViewRoot).getChildAt(0), mContext);
3183bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
3193bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // success!
3203bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            return SceneResult.SUCCESS;
3213bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        } catch (Throwable e) {
3223bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // get the real cause of the exception.
3233bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            Throwable t = e;
3243bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            while (t.getCause() != null) {
3253bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                t = t.getCause();
3263bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            }
3273bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
3283bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // log it
3293bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            mParams.getLogger().error(t);
3303bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
3313bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            return new SceneResult("Unknown error during inflation.", t);
3323bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
3333bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
3343bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
3353bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    /**
3363bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * Compute style information from the given list of style for the project and framework.
3373bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param themeName the name of the current theme.  In order to differentiate project and
3383bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * platform themes sharing the same name, all project themes must be prepended with
3393bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * a '*' character.
3403bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param isProjectTheme Is this a project theme
3413bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param inProjectStyleMap the project style map
3423bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param inFrameworkStyleMap the framework style map
3433bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param outInheritanceMap the map of style inheritance. This is filled by the method
3443bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @return the {@link IStyleResourceValue} matching <var>themeName</var>
3453bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     */
3463bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private IStyleResourceValue computeStyleMaps(
3473bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            String themeName, boolean isProjectTheme, Map<String,
3483bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            IResourceValue> inProjectStyleMap, Map<String, IResourceValue> inFrameworkStyleMap,
3493bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            Map<IStyleResourceValue, IStyleResourceValue> outInheritanceMap) {
3503bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
3513bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (inProjectStyleMap != null && inFrameworkStyleMap != null) {
3523bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // first, get the theme
3533bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            IResourceValue theme = null;
3543bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
3553bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // project theme names have been prepended with a *
3563bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            if (isProjectTheme) {
3573bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                theme = inProjectStyleMap.get(themeName);
3583bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            } else {
3593bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                theme = inFrameworkStyleMap.get(themeName);
3603bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            }
3613bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
3623bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            if (theme instanceof IStyleResourceValue) {
3633bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                // compute the inheritance map for both the project and framework styles
3643bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                computeStyleInheritance(inProjectStyleMap.values(), inProjectStyleMap,
3653bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                        inFrameworkStyleMap, outInheritanceMap);
3663bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
3673bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                // Compute the style inheritance for the framework styles/themes.
3683bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                // Since, for those, the style parent values do not contain 'android:'
3693bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                // we want to force looking in the framework style only to avoid using
3703bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                // similarly named styles from the project.
3713bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                // To do this, we pass null in lieu of the project style map.
3723bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                computeStyleInheritance(inFrameworkStyleMap.values(), null /*inProjectStyleMap */,
3733bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                        inFrameworkStyleMap, outInheritanceMap);
3743bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
3753bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                return (IStyleResourceValue)theme;
3763bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            }
3773bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
3783bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
3793bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        return null;
3803bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
3813bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
3823bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    /**
3833bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * Compute the parent style for all the styles in a given list.
3843bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param styles the styles for which we compute the parent.
3853bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param inProjectStyleMap the map of project styles.
3863bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param inFrameworkStyleMap the map of framework styles.
3873bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param outInheritanceMap the map of style inheritance. This is filled by the method.
3883bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     */
3893bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private void computeStyleInheritance(Collection<IResourceValue> styles,
3903bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            Map<String, IResourceValue> inProjectStyleMap,
3913bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            Map<String, IResourceValue> inFrameworkStyleMap,
3923bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            Map<IStyleResourceValue, IStyleResourceValue> outInheritanceMap) {
3933bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        for (IResourceValue value : styles) {
3943bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            if (value instanceof IStyleResourceValue) {
3953bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                IStyleResourceValue style = (IStyleResourceValue)value;
3963bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                IStyleResourceValue parentStyle = null;
3973bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
3983bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                // first look for a specified parent.
3993bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                String parentName = style.getParentStyle();
4003bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
4013bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                // no specified parent? try to infer it from the name of the style.
4023bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                if (parentName == null) {
4033bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    parentName = getParentName(value.getName());
4043bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                }
4053bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
4063bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                if (parentName != null) {
4073bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    parentStyle = getStyle(parentName, inProjectStyleMap, inFrameworkStyleMap);
4083bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
4093bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    if (parentStyle != null) {
4103bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                        outInheritanceMap.put(style, parentStyle);
4113bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    }
4123bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                }
4133bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            }
4143bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
4153bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
4163bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
4173bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    /**
4183bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * Searches for and returns the {@link IStyleResourceValue} from a given name.
4193bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * <p/>The format of the name can be:
4203bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * <ul>
4213bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * <li>[android:]&lt;name&gt;</li>
4223bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * <li>[android:]style/&lt;name&gt;</li>
4233bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * <li>@[android:]style/&lt;name&gt;</li>
4243bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * </ul>
4253bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param parentName the name of the style.
4263bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param inProjectStyleMap the project style map. Can be <code>null</code>
4273bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param inFrameworkStyleMap the framework style map.
4283bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @return The matching {@link IStyleResourceValue} object or <code>null</code> if not found.
4293bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     */
4303bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private IStyleResourceValue getStyle(String parentName,
4313bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            Map<String, IResourceValue> inProjectStyleMap,
4323bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            Map<String, IResourceValue> inFrameworkStyleMap) {
4333bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        boolean frameworkOnly = false;
4343bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
4353bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        String name = parentName;
4363bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
4373bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // remove the useless @ if it's there
4383bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (name.startsWith(BridgeConstants.PREFIX_RESOURCE_REF)) {
4393bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            name = name.substring(BridgeConstants.PREFIX_RESOURCE_REF.length());
4403bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
4413bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
4423bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // check for framework identifier.
4433bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (name.startsWith(BridgeConstants.PREFIX_ANDROID)) {
4443bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            frameworkOnly = true;
4453bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            name = name.substring(BridgeConstants.PREFIX_ANDROID.length());
4463bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
4473bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
4483bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // at this point we could have the format <type>/<name>. we want only the name as long as
4493bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // the type is style.
4503bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (name.startsWith(BridgeConstants.REFERENCE_STYLE)) {
4513bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            name = name.substring(BridgeConstants.REFERENCE_STYLE.length());
4523bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        } else if (name.indexOf('/') != -1) {
4533bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            return null;
4543bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
4553bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
4563bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        IResourceValue parent = null;
4573bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
4583bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // if allowed, search in the project resources.
4593bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (frameworkOnly == false && inProjectStyleMap != null) {
4603bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            parent = inProjectStyleMap.get(name);
4613bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
4623bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
4633bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // if not found, then look in the framework resources.
4643bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (parent == null) {
4653bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            parent = inFrameworkStyleMap.get(name);
4663bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
4673bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
4683bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // make sure the result is the proper class type and return it.
4693bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (parent instanceof IStyleResourceValue) {
4703bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            return (IStyleResourceValue)parent;
4713bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
4723bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
4733bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        mParams.getLogger().error(
4743bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                String.format("Unable to resolve parent style name: %s", parentName));
4753bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
4763bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        return null;
4773bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
4783bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
4793bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    /**
4803bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * Computes the name of the parent style, or <code>null</code> if the style is a root style.
4813bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     */
4823bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private String getParentName(String styleName) {
4833bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        int index = styleName.lastIndexOf('.');
4843bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (index != -1) {
4853bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            return styleName.substring(0, index);
4863bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
4873bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
4883bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        return null;
4893bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
4903bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
4913bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    /**
4923bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * Returns the top screen offset. This depends on whether the current theme defines the user
4933bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * of the title and status bars.
4943bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param frameworkResources The framework resources
4953bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param currentTheme The current theme
4963bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param context The context
4973bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @return the pixel height offset
4983bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     */
4993bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private int getScreenOffset(Map<String, Map<String, IResourceValue>> frameworkResources,
5003bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            IStyleResourceValue currentTheme, BridgeContext context) {
5013bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        int offset = 0;
5023bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
5033bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // get the title bar flag from the current theme.
5043bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        IResourceValue value = context.findItemInStyle(currentTheme, "windowNoTitle");
5053bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
5063bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // because it may reference something else, we resolve it.
5073bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        value = context.resolveResValue(value);
5083bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
5093bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // if there's a value and it's true (default is false)
5103bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (value == null || value.getValue() == null ||
5113bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                XmlUtils.convertValueToBoolean(value.getValue(), false /* defValue */) == false) {
5123bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // default size of the window title bar
5133bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            int defaultOffset = DEFAULT_TITLE_BAR_HEIGHT;
5143bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
5153bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // get value from the theme.
5163bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            value = context.findItemInStyle(currentTheme, "windowTitleSize");
5173bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
5183bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // resolve it
5193bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            value = context.resolveResValue(value);
5203bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
5213bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            if (value != null) {
5223bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                // get the numerical value, if available
5233bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                TypedValue typedValue = ResourceHelper.getValue(value.getValue());
5243bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                if (typedValue != null) {
5253bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    // compute the pixel value based on the display metrics
5263bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    defaultOffset = (int)typedValue.getDimension(context.getResources().mMetrics);
5273bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                }
5283bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            }
5293bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
5303bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            offset += defaultOffset;
5313bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
5323bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
5333bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // get the fullscreen flag from the current theme.
5343bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        value = context.findItemInStyle(currentTheme, "windowFullscreen");
5353bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
5363bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // because it may reference something else, we resolve it.
5373bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        value = context.resolveResValue(value);
5383bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
5393bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (value == null || value.getValue() == null ||
5403bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                XmlUtils.convertValueToBoolean(value.getValue(), false /* defValue */) == false) {
5413bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
5423bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // default value
5433bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            int defaultOffset = DEFAULT_STATUS_BAR_HEIGHT;
5443bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
5453bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // get the real value, first the list of Dimensions from the framework map
5463bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            Map<String, IResourceValue> dimens = frameworkResources.get(BridgeConstants.RES_DIMEN);
5473bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
5483bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // now get the value
5493bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            value = dimens.get("status_bar_height");
5503bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            if (value != null) {
5513bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                TypedValue typedValue = ResourceHelper.getValue(value.getValue());
5523bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                if (typedValue != null) {
5533bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    // compute the pixel value based on the display metrics
5543bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    defaultOffset = (int)typedValue.getDimension(context.getResources().mMetrics);
5553bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                }
5563bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            }
5573bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
5583bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // add the computed offset.
5593bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            offset += defaultOffset;
5603bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
5613bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
5623bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        return offset;
5633bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
5643bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
5653bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
5663bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    /**
5673bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * Post process on a view hierachy that was just inflated.
5683bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * <p/>At the moment this only support TabHost: If {@link TabHost} is detected, look for the
5693bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * {@link TabWidget}, and the corresponding {@link FrameLayout} and make new tabs automatically
5703bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * based on the content of the {@link FrameLayout}.
5713bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param view the root view to process.
5723bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param projectCallback callback to the project.
5733bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     */
5743bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private void postInflateProcess(View view, IProjectCallback projectCallback)
5753bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            throws PostInflateException {
5763bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (view instanceof TabHost) {
5773bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            setupTabHost((TabHost)view, projectCallback);
5783bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        } else if (view instanceof ViewGroup) {
5793bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            ViewGroup group = (ViewGroup)view;
5803bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            final int count = group.getChildCount();
5813bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            for (int c = 0 ; c < count ; c++) {
5823bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                View child = group.getChildAt(c);
5833bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                postInflateProcess(child, projectCallback);
5843bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            }
5853bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
5863bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
5873bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
5883bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    /**
5893bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * Sets up a {@link TabHost} object.
5903bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param tabHost the TabHost to setup.
5913bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param projectCallback The project callback object to access the project R class.
5923bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @throws PostInflateException
5933bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     */
5943bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private void setupTabHost(TabHost tabHost, IProjectCallback projectCallback)
5953bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            throws PostInflateException {
5963bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // look for the TabWidget, and the FrameLayout. They have their own specific names
5973bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        View v = tabHost.findViewById(android.R.id.tabs);
5983bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
5993bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (v == null) {
6003bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            throw new PostInflateException(
6013bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    "TabHost requires a TabWidget with id \"android:id/tabs\".\n");
6023bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
6033bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6043bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if ((v instanceof TabWidget) == false) {
6053bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            throw new PostInflateException(String.format(
6063bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    "TabHost requires a TabWidget with id \"android:id/tabs\".\n" +
6073bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    "View found with id 'tabs' is '%s'", v.getClass().getCanonicalName()));
6083bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
6093bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6103bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        v = tabHost.findViewById(android.R.id.tabcontent);
6113bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6123bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (v == null) {
6133bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // TODO: see if we can fake tabs even without the FrameLayout (same below when the framelayout is empty)
6143bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            throw new PostInflateException(
6153bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    "TabHost requires a FrameLayout with id \"android:id/tabcontent\".");
6163bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
6173bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6183bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if ((v instanceof FrameLayout) == false) {
6193bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            throw new PostInflateException(String.format(
6203bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    "TabHost requires a FrameLayout with id \"android:id/tabcontent\".\n" +
6213bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    "View found with id 'tabcontent' is '%s'", v.getClass().getCanonicalName()));
6223bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
6233bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6243bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        FrameLayout content = (FrameLayout)v;
6253bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6263bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // now process the content of the framelayout and dynamically create tabs for it.
6273bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        final int count = content.getChildCount();
6283bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6293bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (count == 0) {
6303bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            throw new PostInflateException(
6313bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    "The FrameLayout for the TabHost has no content. Rendering failed.\n");
6323bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
6333bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6343bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // this must be called before addTab() so that the TabHost searches its TabWidget
6353bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // and FrameLayout.
6363bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        tabHost.setup();
6373bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6383bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // for each child of the framelayout, add a new TabSpec
6393bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        for (int i = 0 ; i < count ; i++) {
6403bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            View child = content.getChildAt(i);
6413bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            String tabSpec = String.format("tab_spec%d", i+1);
6423bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            int id = child.getId();
6433bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            String[] resource = projectCallback.resolveResourceValue(id);
6443bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            String name;
6453bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            if (resource != null) {
6463bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                name = resource[0]; // 0 is resource name, 1 is resource type.
6473bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            } else {
6483bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                name = String.format("Tab %d", i+1); // default name if id is unresolved.
6493bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            }
6503bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            tabHost.addTab(tabHost.newTabSpec(tabSpec).setIndicator(name).setContent(id));
6513bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
6523bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
6533bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6543bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6553bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    /**
6563bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * Visits a View and its children and generate a {@link ViewInfo} containing the
6573bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * bounds of all the views.
6583bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param view the root View
6593bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param context the context.
6603bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     */
6613bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private ViewInfo visit(View view, BridgeContext context) {
6623bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (view == null) {
6633bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            return null;
6643bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
6653bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6663bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        ViewInfo result = new ViewInfo(view.getClass().getName(),
6673bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                context.getViewKey(view),
6682d240967c9bc38cbf69967457b33f953f8826e96Xavier Ducrohet                view.getLeft(), view.getTop(), view.getRight(), view.getBottom(),
6692d240967c9bc38cbf69967457b33f953f8826e96Xavier Ducrohet                view, view.getLayoutParams());
6703bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6713bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (view instanceof ViewGroup) {
6723bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            ViewGroup group = ((ViewGroup) view);
6733bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            List<ViewInfo> children = new ArrayList<ViewInfo>();
6743bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            for (int i = 0; i < group.getChildCount(); i++) {
6753bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                children.add(visit(group.getChildAt(i), context));
6763bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            }
6773bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            result.setChildren(children);
6783bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
6793bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6803bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        return result;
6813bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
6823bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6833bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    public BufferedImage getImage() {
6843bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        return mImage;
6853bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
6863bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6873bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    public ViewInfo getViewInfo() {
6883bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        return mViewInfo;
6893bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
6902d240967c9bc38cbf69967457b33f953f8826e96Xavier Ducrohet
6912d240967c9bc38cbf69967457b33f953f8826e96Xavier Ducrohet    public Map<String, String> getDefaultViewPropertyValues(Object viewObject) {
6922d240967c9bc38cbf69967457b33f953f8826e96Xavier Ducrohet        return mContext.getDefaultPropMap(viewObject);
6932d240967c9bc38cbf69967457b33f953f8826e96Xavier Ducrohet    }
6943bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet}
695