RenderSessionImpl.java revision ddea50d03cdda807bbaea54beffd7a341c51f770
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
19ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohetimport static com.android.layoutlib.api.SceneResult.SceneStatus.ERROR_LOCK_INTERRUPTED;
20ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohetimport static com.android.layoutlib.api.SceneResult.SceneStatus.ERROR_TIMEOUT;
21ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohetimport static com.android.layoutlib.api.SceneResult.SceneStatus.SUCCESS;
22ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet
233bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport com.android.internal.util.XmlUtils;
243bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport com.android.layoutlib.api.IProjectCallback;
253bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport com.android.layoutlib.api.IResourceValue;
263bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport com.android.layoutlib.api.IStyleResourceValue;
273bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport com.android.layoutlib.api.LayoutBridge;
283bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport com.android.layoutlib.api.SceneParams;
293bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport com.android.layoutlib.api.SceneResult;
303bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport com.android.layoutlib.api.ViewInfo;
313bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport com.android.layoutlib.api.IDensityBasedResourceValue.Density;
32ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohetimport com.android.layoutlib.api.LayoutScene.IAnimationListener;
33e489a969de010782fcc0ab048c7357d42ada9400Xavier Ducrohetimport com.android.layoutlib.api.SceneParams.RenderingMode;
34ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohetimport com.android.layoutlib.bridge.Bridge;
353bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport com.android.layoutlib.bridge.BridgeConstants;
363bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport com.android.layoutlib.bridge.android.BridgeContext;
373bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport com.android.layoutlib.bridge.android.BridgeInflater;
383bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport com.android.layoutlib.bridge.android.BridgeWindow;
393bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport com.android.layoutlib.bridge.android.BridgeWindowSession;
403bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
413bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
42ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohetimport android.animation.AnimatorInflater;
43ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohetimport android.animation.ObjectAnimator;
443bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.app.Fragment_Delegate;
453bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.graphics.Bitmap;
463bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.graphics.Bitmap_Delegate;
473bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.graphics.Canvas;
483bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.graphics.Canvas_Delegate;
493bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.graphics.drawable.Drawable;
503bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.os.Handler;
513bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.os.Looper;
523bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.util.DisplayMetrics;
533bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.util.TypedValue;
543bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.view.View;
553bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.view.ViewGroup;
563bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.view.View.AttachInfo;
573bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.view.View.MeasureSpec;
583bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.widget.FrameLayout;
593bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.widget.TabHost;
603bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.widget.TabWidget;
613bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
623bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport java.awt.Color;
633bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport java.awt.Graphics2D;
643bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport java.awt.image.BufferedImage;
653bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport java.util.ArrayList;
663bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport java.util.Collection;
673bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport java.util.HashMap;
683bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport java.util.List;
693bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport java.util.Map;
70ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohetimport java.util.concurrent.TimeUnit;
71ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohetimport java.util.concurrent.locks.ReentrantLock;
723bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
733bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet/**
743bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet * Class managing a layout "scene".
753bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet *
763bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet * A scene is a stateful representation of a layout file. It is initialized with data coming through
773bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet * the {@link LayoutBridge} API to inflate the layout. Further actions and rendering can then
783bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet * be done on the layout.
793bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet *
803bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet */
813bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetpublic class LayoutSceneImpl {
823bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
833bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private static final int DEFAULT_TITLE_BAR_HEIGHT = 25;
843bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private static final int DEFAULT_STATUS_BAR_HEIGHT = 25;
853bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
86ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet    /**
87ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * The current context being rendered. This is set through {@link #acquire(long)} and
88ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * {@link #init(long)}, and unset in {@link #release()}.
89ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     */
90ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet    private static BridgeContext sCurrentContext = null;
91ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet
923bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private final SceneParams mParams;
933bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
943bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    // scene state
953bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private BridgeContext mContext;
963bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private BridgeXmlBlockParser mBlockParser;
973bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private BridgeInflater mInflater;
983bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private IStyleResourceValue mCurrentTheme;
993bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private int mScreenOffset;
1003bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private IResourceValue mWindowBackground;
1013bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private FrameLayout mViewRoot;
1023bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
1033bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    // information being returned through the API
1043bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private BufferedImage mImage;
1053bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private ViewInfo mViewInfo;
1063bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
1073bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private static final class PostInflateException extends Exception {
1083bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        private static final long serialVersionUID = 1L;
1093bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
1103bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        public PostInflateException(String message) {
1113bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            super(message);
1123bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
1133bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
1143bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
1153bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    /**
1163bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * Creates a layout scene with all the information coming from the layout bridge API.
1173bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * <p>
118ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * This <b>must</b> be followed by a call to {@link LayoutSceneImpl#init()}, which act as a
119ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * call to {@link LayoutSceneImpl#acquire(long)}
1203bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     *
1213bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @see LayoutBridge#createScene(com.android.layoutlib.api.SceneParams)
1223bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     */
1233bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    public LayoutSceneImpl(SceneParams params) {
1243bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // copy the params.
1253bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        mParams = new SceneParams(params);
126ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet    }
127ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet
128ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet    /**
129ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * Initializes and acquires the scene, creating various Android objects such as context,
130ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * inflater, and parser.
131ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     *
132ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * @param timeout the time to wait if another rendering is happening.
133ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     *
134ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * @return whether the scene was prepared
135ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     *
136ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * @see #acquire(long)
137ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * @see #release()
138ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     */
139ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet    public SceneResult init(long timeout) {
140ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        // acquire the lock. if the result is null, lock was just acquired, otherwise, return
141ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        // the result.
142ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        SceneResult result = acquireLock(timeout);
143ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        if (result != null) {
144ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet            return result;
145ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        }
1463bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
1473bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // setup the display Metrics.
1483bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        DisplayMetrics metrics = new DisplayMetrics();
1493bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        metrics.densityDpi = mParams.getDensity();
1503bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        metrics.density = mParams.getDensity() / (float) DisplayMetrics.DENSITY_DEFAULT;
1513bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        metrics.scaledDensity = metrics.density;
1523bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        metrics.widthPixels = mParams.getScreenWidth();
1533bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        metrics.heightPixels = mParams.getScreenHeight();
1543bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        metrics.xdpi = mParams.getXdpi();
1553bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        metrics.ydpi = mParams.getYdpi();
1563bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
1573bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // find the current theme and compute the style inheritance map
1583bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        Map<IStyleResourceValue, IStyleResourceValue> styleParentMap =
1593bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            new HashMap<IStyleResourceValue, IStyleResourceValue>();
1603bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
1613bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        mCurrentTheme = computeStyleMaps(mParams.getThemeName(), mParams.getIsProjectTheme(),
1623bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                mParams.getProjectResources().get(BridgeConstants.RES_STYLE),
1633bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                mParams.getFrameworkResources().get(BridgeConstants.RES_STYLE), styleParentMap);
1643bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
1653bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // build the context
1663bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        mContext = new BridgeContext(mParams.getProjectKey(), metrics, mCurrentTheme,
1673bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                mParams.getProjectResources(), mParams.getFrameworkResources(),
1683bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                styleParentMap, mParams.getProjectCallback(), mParams.getLogger());
1693bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
170ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        // set the current rendering context
171ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        sCurrentContext = mContext;
172ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet
1733bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // make sure the Resources object references the context (and other objects) for this
1743bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // scene
1753bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        mContext.initResources();
1763bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
1773bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // get the screen offset and window-background resource
1783bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        mWindowBackground = null;
1793bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        mScreenOffset = 0;
1803bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (mCurrentTheme != null && mParams.isCustomBackgroundEnabled() == false) {
1813bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            mWindowBackground = mContext.findItemInStyle(mCurrentTheme, "windowBackground");
1823bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            mWindowBackground = mContext.resolveResValue(mWindowBackground);
1833bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
184ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet            mScreenOffset = getScreenOffset(mParams.getFrameworkResources(), mCurrentTheme,
185ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet                    mContext);
1863bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
1873bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
1883bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // build the inflater and parser.
1893bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        mInflater = new BridgeInflater(mContext, mParams.getProjectCallback());
1903bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        mContext.setBridgeInflater(mInflater);
1913bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        mInflater.setFactory2(mContext);
1923bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
1933bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        mBlockParser = new BridgeXmlBlockParser(mParams.getLayoutDescription(),
1943bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                mContext, false /* platformResourceFlag */);
195ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet
196ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        return SceneResult.SUCCESS;
1973bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
1983bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
1993bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    /**
200ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * Prepares the current thread for rendering.
201ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     *
202ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * Note that while this can be called several time, the first call to {@link #cleanupThread()}
203ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * will do the clean-up, and make the thread unable to do further scene actions.
2043bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     */
205ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet    public void prepareThread() {
2063bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // we need to make sure the Looper has been initialized for this thread.
2073bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // this is required for View that creates Handler objects.
2083bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (Looper.myLooper() == null) {
2093bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            Looper.prepare();
2103bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
211ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet    }
212ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet
213ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet    /**
214ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * Cleans up thread-specific data. After this, the thread cannot be used for scene actions.
215ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * <p>
216ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * Note that it doesn't matter how many times {@link #prepareThread()} was called, a single
217ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * call to this will prevent the thread from doing further scene actions
218ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     */
219ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet    public void cleanupThread() {
220ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        // clean up the looper
221ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        Looper.sThreadLocal.remove();
222ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet    }
223ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet
224ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet    /**
225ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * Prepares the scene for action.
226ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * <p>
227ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * This call is blocking if another rendering/inflating is currently happening, and will return
228ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * whether the preparation worked.
229ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     *
230ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * The preparation can fail if another rendering took too long and the timeout was elapsed.
231ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     *
232ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * More than one call to this from the same thread will have no effect and will return
233ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * {@link SceneResult#SUCCESS}.
234ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     *
235ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * After scene actions have taken place, only one call to {@link #release()} must be
236ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * done.
237ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     *
238ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * @param timeout the time to wait if another rendering is happening.
239ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     *
240ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * @return whether the scene was prepared
241ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     *
242ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * @see #release()
243ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     *
244ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * @throws IllegalStateException if {@link #init(long)} was never called.
245ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     */
246ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet    public SceneResult acquire(long timeout) {
247ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        if (mContext == null) {
248ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet            throw new IllegalStateException("After scene creation, #init() must be called");
249ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        }
250ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet
251ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        // acquire the lock. if the result is null, lock was just acquired, otherwise, return
252ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        // the result.
253ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        SceneResult result = acquireLock(timeout);
254ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        if (result != null) {
255ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet            return result;
256ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        }
2573bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
2583bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // make sure the Resources object references the context (and other objects) for this
2593bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // scene
2603bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        mContext.initResources();
261ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        sCurrentContext = mContext;
262ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet
263ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        return SUCCESS.getResult();
2643bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
2653bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
2663bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    /**
267ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * Acquire the lock so that the scene can be acted upon.
2683bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * <p>
269ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * This returns null if the lock was just acquired, otherwise it returns
270ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * {@link SceneResult#SUCCESS} if the lock already belonged to that thread, or another
271ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * instance (see {@link SceneResult#getStatus()}) if an error occurred.
272ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     *
273ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * @param timeout the time to wait if another rendering is happening.
274ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * @return null if the lock was just acquire or another result depending on the state.
275ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     *
276ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * @throws IllegalStateException if the current context is different than the one owned by
277ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     *      the scene.
2783bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     */
279ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet    private SceneResult acquireLock(long timeout) {
280ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        ReentrantLock lock = Bridge.getLock();
281ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        if (lock.isHeldByCurrentThread() == false) {
282ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet            try {
283ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet                boolean acquired = lock.tryLock(timeout, TimeUnit.MILLISECONDS);
284ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet
285ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet                if (acquired == false) {
286ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet                    return ERROR_TIMEOUT.getResult();
287ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet                }
288ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet            } catch (InterruptedException e) {
289ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet                return ERROR_LOCK_INTERRUPTED.getResult();
290ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet            }
291ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        } else {
292ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet            // This thread holds the lock already. Checks that this wasn't for a different context.
293ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet            // If this is called by init, mContext will be null and so should sCurrentContext
294ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet            // anyway
295ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet            if (mContext != sCurrentContext) {
296ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet                throw new IllegalStateException("Acquiring different scenes from same thread without releases");
297ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet            }
298ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet            return SUCCESS.getResult();
299ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        }
3003bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
301ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        return null;
302ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet    }
303ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet
304ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet    /**
305ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * Cleans up the scene after an action.
306ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     */
307ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet    public void release() {
308ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        ReentrantLock lock = Bridge.getLock();
309ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet
310ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        // with the use of finally blocks, it is possible to find ourself calling this
311ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        // without a successful call to prepareScene. This test makes sure that unlock() will
312ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        // not throw IllegalMonitorStateException.
313ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        if (lock.isHeldByCurrentThread()) {
314ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet            // Make sure to remove static references, otherwise we could not unload the lib
315ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet            mContext.disposeResources();
316ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet            sCurrentContext = null;
317ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet
318ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet            lock.unlock();
319ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        }
3203bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
3213bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
3223bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    /**
3233bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * Inflates the layout.
3243bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * <p>
325ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * {@link #acquire(long)} must have been called before this.
326ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     *
327ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * @throws IllegalStateException if the current context is different than the one owned by
328ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     *      the scene, or if {@link #init(long)} was not called.
3293bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     */
3303bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    public SceneResult inflate() {
331ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        checkLock();
332ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet
3333bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        try {
3343bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
3353bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            mViewRoot = new FrameLayout(mContext);
3363bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
3373bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // Sets the project callback (custom view loader) to the fragment delegate so that
3383bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // it can instantiate the custom Fragment.
3393bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            Fragment_Delegate.setProjectCallback(mParams.getProjectCallback());
3403bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
3413bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            View view = mInflater.inflate(mBlockParser, mViewRoot);
3423bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
3433bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // post-inflate process. For now this supports TabHost/TabWidget
3443bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            postInflateProcess(view, mParams.getProjectCallback());
3453bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
3463bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            Fragment_Delegate.setProjectCallback(null);
3473bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
3483bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // set the AttachInfo on the root view.
3493bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            AttachInfo info = new AttachInfo(new BridgeWindowSession(), new BridgeWindow(),
3503bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    new Handler(), null);
3513bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            info.mHasWindowFocus = true;
3523bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            info.mWindowVisibility = View.VISIBLE;
3533bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            info.mInTouchMode = false; // this is so that we can display selections.
3543bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            mViewRoot.dispatchAttachedToWindow(info, 0);
3553bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
3563bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // get the background drawable
3573bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            if (mWindowBackground != null) {
3583bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                Drawable d = ResourceHelper.getDrawable(mWindowBackground,
3593bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                        mContext, true /* isFramework */);
3603bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                mViewRoot.setBackgroundDrawable(d);
3613bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            }
3623bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
3633bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            return SceneResult.SUCCESS;
3643bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        } catch (PostInflateException e) {
3653bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            return new SceneResult("Error during post inflation process:\n" + e.getMessage());
3663bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        } catch (Throwable e) {
3673bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // get the real cause of the exception.
3683bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            Throwable t = e;
3693bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            while (t.getCause() != null) {
3703bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                t = t.getCause();
3713bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            }
3723bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
3733bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // log it
3743bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            mParams.getLogger().error(t);
3753bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
3763bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            return new SceneResult("Unknown error during inflation.", t);
3773bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
3783bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
3793bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
3803bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    /**
3813bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * Renders the scene.
3823bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * <p>
383ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * {@link #acquire(long)} must have been called before this.
384ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     *
385ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * @throws IllegalStateException if the current context is different than the one owned by
386ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     *      the scene, or if {@link #acquire(long)} was not called.
3873bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     */
3883bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    public SceneResult render() {
389ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        checkLock();
390ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet
3913bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        try {
392ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet            long current = System.currentTimeMillis();
3933bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            if (mViewRoot == null) {
3943bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                return new SceneResult("Layout has not been inflated!");
3953bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            }
3963bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // measure the views
3973bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            int w_spec, h_spec;
3983bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
3993bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            int renderScreenWidth = mParams.getScreenWidth();
4003bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            int renderScreenHeight = mParams.getScreenHeight();
4013bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
402e489a969de010782fcc0ab048c7357d42ada9400Xavier Ducrohet            RenderingMode renderingMode = mParams.getRenderingMode();
403e489a969de010782fcc0ab048c7357d42ada9400Xavier Ducrohet
404e489a969de010782fcc0ab048c7357d42ada9400Xavier Ducrohet            if (renderingMode != RenderingMode.NORMAL) {
4053bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                // measure the full size needed by the layout.
4063bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                w_spec = MeasureSpec.makeMeasureSpec(renderScreenWidth,
407e489a969de010782fcc0ab048c7357d42ada9400Xavier Ducrohet                        renderingMode.isHorizExpand() ?
408e489a969de010782fcc0ab048c7357d42ada9400Xavier Ducrohet                                MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
409e489a969de010782fcc0ab048c7357d42ada9400Xavier Ducrohet                                : MeasureSpec.EXACTLY);
4103bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                h_spec = MeasureSpec.makeMeasureSpec(renderScreenHeight - mScreenOffset,
411e489a969de010782fcc0ab048c7357d42ada9400Xavier Ducrohet                        renderingMode.isVertExpand() ?
412e489a969de010782fcc0ab048c7357d42ada9400Xavier Ducrohet                                MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
413e489a969de010782fcc0ab048c7357d42ada9400Xavier Ducrohet                                : MeasureSpec.EXACTLY);
4143bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                mViewRoot.measure(w_spec, h_spec);
4153bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
416e489a969de010782fcc0ab048c7357d42ada9400Xavier Ducrohet                if (renderingMode.isHorizExpand()) {
417e489a969de010782fcc0ab048c7357d42ada9400Xavier Ducrohet                    int neededWidth = mViewRoot.getChildAt(0).getMeasuredWidth();
418e489a969de010782fcc0ab048c7357d42ada9400Xavier Ducrohet                    if (neededWidth > renderScreenWidth) {
419e489a969de010782fcc0ab048c7357d42ada9400Xavier Ducrohet                        renderScreenWidth = neededWidth;
420e489a969de010782fcc0ab048c7357d42ada9400Xavier Ducrohet                    }
4213bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                }
4223bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
423e489a969de010782fcc0ab048c7357d42ada9400Xavier Ducrohet                if (renderingMode.isVertExpand()) {
424e489a969de010782fcc0ab048c7357d42ada9400Xavier Ducrohet                    int neededHeight = mViewRoot.getChildAt(0).getMeasuredHeight();
425e489a969de010782fcc0ab048c7357d42ada9400Xavier Ducrohet                    if (neededHeight > renderScreenHeight - mScreenOffset) {
426e489a969de010782fcc0ab048c7357d42ada9400Xavier Ducrohet                        renderScreenHeight = neededHeight + mScreenOffset;
427e489a969de010782fcc0ab048c7357d42ada9400Xavier Ducrohet                    }
4283bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                }
4293bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            }
4303bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
4313bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // remeasure with the size we need
4323bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // This must always be done before the call to layout
4333bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            w_spec = MeasureSpec.makeMeasureSpec(renderScreenWidth, MeasureSpec.EXACTLY);
4343bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            h_spec = MeasureSpec.makeMeasureSpec(renderScreenHeight - mScreenOffset,
4353bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    MeasureSpec.EXACTLY);
4363bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            mViewRoot.measure(w_spec, h_spec);
4373bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
4383bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // now do the layout.
4393bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            mViewRoot.layout(0, mScreenOffset, renderScreenWidth, renderScreenHeight);
4403bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
4413bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // draw the views
4423bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // create the BufferedImage into which the layout will be rendered.
4433bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            mImage = new BufferedImage(renderScreenWidth, renderScreenHeight - mScreenOffset,
4443bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    BufferedImage.TYPE_INT_ARGB);
4453bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
4463bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            if (mParams.isCustomBackgroundEnabled()) {
4473bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                Graphics2D gc = mImage.createGraphics();
4488396d6f3a1f8cb397a1ef54f0a38893d64d7c275Tor Norbye                gc.setColor(new Color(mParams.getCustomBackgroundColor(), true));
4493bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                gc.fillRect(0, 0, renderScreenWidth, renderScreenHeight - mScreenOffset);
4503bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                gc.dispose();
4513bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            }
4523bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
4533bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // create an Android bitmap around the BufferedImage
4543bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            Bitmap bitmap = Bitmap_Delegate.createBitmap(mImage,
455e4d978793ab8b4fdf88c7a6235d9ba17860c7bdaXavier Ducrohet                    true /*isMutable*/,
4563bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    Density.getEnum(mParams.getDensity()));
4573bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
4583bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // create a Canvas around the Android bitmap
4593bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            Canvas canvas = new Canvas(bitmap);
460e4d978793ab8b4fdf88c7a6235d9ba17860c7bdaXavier Ducrohet            canvas.setDensity(mParams.getDensity());
4613bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
4623bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // to set the logger, get the native delegate
4633bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas);
4643bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            canvasDelegate.setLogger(mParams.getLogger());
4653bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
4663bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            mViewRoot.draw(canvas);
4673bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            canvasDelegate.dispose();
4683bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
4693bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            mViewInfo = visit(((ViewGroup)mViewRoot).getChildAt(0), mContext);
4703bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
471ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet            System.out.println("rendering (ms): " + (System.currentTimeMillis() - current));
472ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet
4733bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // success!
4743bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            return SceneResult.SUCCESS;
4753bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        } catch (Throwable e) {
4763bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // get the real cause of the exception.
4773bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            Throwable t = e;
4783bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            while (t.getCause() != null) {
4793bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                t = t.getCause();
4803bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            }
4813bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
4823bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // log it
4833bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            mParams.getLogger().error(t);
4843bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
4853bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            return new SceneResult("Unknown error during inflation.", t);
4863bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
4873bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
4883bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
4893bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    /**
490ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * Animate an object
491ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * <p>
492ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * {@link #acquire(long)} must have been called before this.
493ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     *
494ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * @throws IllegalStateException if the current context is different than the one owned by
495ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     *      the scene, or if {@link #acquire(long)} was not called.
496ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     */
497ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet    public SceneResult animate(Object targetObject, String animationName,
498ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet            boolean isFrameworkAnimation, IAnimationListener listener) {
499ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        checkLock();
500ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet
501ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        // find the animation file.
502ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        IResourceValue animationResource = null;
503ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        int animationId = 0;
504ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        if (isFrameworkAnimation) {
505ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet            animationResource = mContext.getFrameworkResource("anim", animationName);
506ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet            if (animationResource != null) {
507ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet                animationId = Bridge.getResourceValue("anim", animationName);
508ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet            }
509ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        } else {
510ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet            animationResource = mContext.getProjectResource("anim", animationName);
511ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet            if (animationResource != null) {
512ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet                animationId = mContext.getProjectCallback().getResourceValue("anim", animationName);
513ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet            }
514ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        }
515ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet
516ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        if (animationResource != null) {
517ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet            try {
518ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet                ObjectAnimator anim = (ObjectAnimator) AnimatorInflater.loadAnimator(
519ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet                        mContext, animationId);
520ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet                if (anim != null) {
521ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet                    anim.setTarget(targetObject);
522ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet
523ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet                    new AnimationThread(this, anim, listener).start();
524ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet
525ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet                    return SceneResult.SUCCESS;
526ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet                }
527ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet            } catch (Exception e) {
528ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet                e.printStackTrace();
529ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet                return new SceneResult("", e);
530ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet            }
531ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        }
532ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet
533ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        return new SceneResult("Failed to find animation");
534ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet    }
535ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet
536ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet    /**
537ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * Checks that the lock is owned by the current thread and that the current context is the one
538ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * from this scene.
539ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     *
540ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * @throws IllegalStateException if the current context is different than the one owned by
541ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     *      the scene, or if {@link #acquire(long)} was not called.
542ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     */
543ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet    private void checkLock() {
544ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        ReentrantLock lock = Bridge.getLock();
545ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        if (lock.isHeldByCurrentThread() == false) {
546ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet            throw new IllegalStateException("scene must be acquired first. see #acquire(long)");
547ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        }
548ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        if (sCurrentContext != mContext) {
549ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet            throw new IllegalStateException("Thread acquired a scene but is rendering a different one");
550ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        }
551ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet    }
552ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet
553ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet
554ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet    /**
5553bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * Compute style information from the given list of style for the project and framework.
5563bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param themeName the name of the current theme.  In order to differentiate project and
5573bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * platform themes sharing the same name, all project themes must be prepended with
5583bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * a '*' character.
5593bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param isProjectTheme Is this a project theme
5603bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param inProjectStyleMap the project style map
5613bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param inFrameworkStyleMap the framework style map
5623bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param outInheritanceMap the map of style inheritance. This is filled by the method
5633bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @return the {@link IStyleResourceValue} matching <var>themeName</var>
5643bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     */
5653bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private IStyleResourceValue computeStyleMaps(
5663bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            String themeName, boolean isProjectTheme, Map<String,
5673bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            IResourceValue> inProjectStyleMap, Map<String, IResourceValue> inFrameworkStyleMap,
5683bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            Map<IStyleResourceValue, IStyleResourceValue> outInheritanceMap) {
5693bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
5703bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (inProjectStyleMap != null && inFrameworkStyleMap != null) {
5713bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // first, get the theme
5723bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            IResourceValue theme = null;
5733bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
5743bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // project theme names have been prepended with a *
5753bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            if (isProjectTheme) {
5763bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                theme = inProjectStyleMap.get(themeName);
5773bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            } else {
5783bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                theme = inFrameworkStyleMap.get(themeName);
5793bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            }
5803bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
5813bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            if (theme instanceof IStyleResourceValue) {
5823bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                // compute the inheritance map for both the project and framework styles
5833bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                computeStyleInheritance(inProjectStyleMap.values(), inProjectStyleMap,
5843bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                        inFrameworkStyleMap, outInheritanceMap);
5853bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
5863bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                // Compute the style inheritance for the framework styles/themes.
5873bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                // Since, for those, the style parent values do not contain 'android:'
5883bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                // we want to force looking in the framework style only to avoid using
5893bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                // similarly named styles from the project.
5903bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                // To do this, we pass null in lieu of the project style map.
5913bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                computeStyleInheritance(inFrameworkStyleMap.values(), null /*inProjectStyleMap */,
5923bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                        inFrameworkStyleMap, outInheritanceMap);
5933bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
5943bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                return (IStyleResourceValue)theme;
5953bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            }
5963bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
5973bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
5983bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        return null;
5993bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
6003bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6013bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    /**
6023bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * Compute the parent style for all the styles in a given list.
6033bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param styles the styles for which we compute the parent.
6043bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param inProjectStyleMap the map of project styles.
6053bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param inFrameworkStyleMap the map of framework styles.
6063bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param outInheritanceMap the map of style inheritance. This is filled by the method.
6073bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     */
6083bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private void computeStyleInheritance(Collection<IResourceValue> styles,
6093bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            Map<String, IResourceValue> inProjectStyleMap,
6103bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            Map<String, IResourceValue> inFrameworkStyleMap,
6113bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            Map<IStyleResourceValue, IStyleResourceValue> outInheritanceMap) {
6123bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        for (IResourceValue value : styles) {
6133bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            if (value instanceof IStyleResourceValue) {
6143bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                IStyleResourceValue style = (IStyleResourceValue)value;
6153bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                IStyleResourceValue parentStyle = null;
6163bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6173bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                // first look for a specified parent.
6183bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                String parentName = style.getParentStyle();
6193bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6203bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                // no specified parent? try to infer it from the name of the style.
6213bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                if (parentName == null) {
6223bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    parentName = getParentName(value.getName());
6233bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                }
6243bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6253bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                if (parentName != null) {
6263bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    parentStyle = getStyle(parentName, inProjectStyleMap, inFrameworkStyleMap);
6273bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6283bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    if (parentStyle != null) {
6293bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                        outInheritanceMap.put(style, parentStyle);
6303bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    }
6313bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                }
6323bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            }
6333bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
6343bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
6353bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6363bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    /**
6373bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * Searches for and returns the {@link IStyleResourceValue} from a given name.
6383bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * <p/>The format of the name can be:
6393bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * <ul>
6403bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * <li>[android:]&lt;name&gt;</li>
6413bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * <li>[android:]style/&lt;name&gt;</li>
6423bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * <li>@[android:]style/&lt;name&gt;</li>
6433bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * </ul>
6443bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param parentName the name of the style.
6453bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param inProjectStyleMap the project style map. Can be <code>null</code>
6463bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param inFrameworkStyleMap the framework style map.
6473bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @return The matching {@link IStyleResourceValue} object or <code>null</code> if not found.
6483bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     */
6493bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private IStyleResourceValue getStyle(String parentName,
6503bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            Map<String, IResourceValue> inProjectStyleMap,
6513bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            Map<String, IResourceValue> inFrameworkStyleMap) {
6523bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        boolean frameworkOnly = false;
6533bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6543bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        String name = parentName;
6553bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6563bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // remove the useless @ if it's there
6573bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (name.startsWith(BridgeConstants.PREFIX_RESOURCE_REF)) {
6583bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            name = name.substring(BridgeConstants.PREFIX_RESOURCE_REF.length());
6593bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
6603bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6613bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // check for framework identifier.
6623bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (name.startsWith(BridgeConstants.PREFIX_ANDROID)) {
6633bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            frameworkOnly = true;
6643bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            name = name.substring(BridgeConstants.PREFIX_ANDROID.length());
6653bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
6663bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6673bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // at this point we could have the format <type>/<name>. we want only the name as long as
6683bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // the type is style.
6693bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (name.startsWith(BridgeConstants.REFERENCE_STYLE)) {
6703bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            name = name.substring(BridgeConstants.REFERENCE_STYLE.length());
6713bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        } else if (name.indexOf('/') != -1) {
6723bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            return null;
6733bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
6743bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6753bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        IResourceValue parent = null;
6763bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6773bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // if allowed, search in the project resources.
6783bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (frameworkOnly == false && inProjectStyleMap != null) {
6793bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            parent = inProjectStyleMap.get(name);
6803bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
6813bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6823bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // if not found, then look in the framework resources.
6833bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (parent == null) {
6843bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            parent = inFrameworkStyleMap.get(name);
6853bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
6863bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6873bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // make sure the result is the proper class type and return it.
6883bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (parent instanceof IStyleResourceValue) {
6893bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            return (IStyleResourceValue)parent;
6903bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
6913bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6923bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        mParams.getLogger().error(
6933bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                String.format("Unable to resolve parent style name: %s", parentName));
6943bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6953bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        return null;
6963bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
6973bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6983bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    /**
6993bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * Computes the name of the parent style, or <code>null</code> if the style is a root style.
7003bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     */
7013bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private String getParentName(String styleName) {
7023bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        int index = styleName.lastIndexOf('.');
7033bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (index != -1) {
7043bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            return styleName.substring(0, index);
7053bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
7063bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
7073bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        return null;
7083bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
7093bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
7103bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    /**
7113bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * Returns the top screen offset. This depends on whether the current theme defines the user
7123bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * of the title and status bars.
7133bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param frameworkResources The framework resources
7143bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param currentTheme The current theme
7153bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param context The context
7163bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @return the pixel height offset
7173bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     */
7183bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private int getScreenOffset(Map<String, Map<String, IResourceValue>> frameworkResources,
7193bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            IStyleResourceValue currentTheme, BridgeContext context) {
7203bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        int offset = 0;
7213bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
7223bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // get the title bar flag from the current theme.
7233bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        IResourceValue value = context.findItemInStyle(currentTheme, "windowNoTitle");
7243bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
7253bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // because it may reference something else, we resolve it.
7263bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        value = context.resolveResValue(value);
7273bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
7283bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // if there's a value and it's true (default is false)
7293bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (value == null || value.getValue() == null ||
7303bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                XmlUtils.convertValueToBoolean(value.getValue(), false /* defValue */) == false) {
7313bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // default size of the window title bar
7323bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            int defaultOffset = DEFAULT_TITLE_BAR_HEIGHT;
7333bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
7343bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // get value from the theme.
7353bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            value = context.findItemInStyle(currentTheme, "windowTitleSize");
7363bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
7373bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // resolve it
7383bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            value = context.resolveResValue(value);
7393bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
7403bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            if (value != null) {
7413bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                // get the numerical value, if available
7423bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                TypedValue typedValue = ResourceHelper.getValue(value.getValue());
7433bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                if (typedValue != null) {
7443bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    // compute the pixel value based on the display metrics
7453bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    defaultOffset = (int)typedValue.getDimension(context.getResources().mMetrics);
7463bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                }
7473bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            }
7483bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
7493bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            offset += defaultOffset;
7503bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
7513bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
7523bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // get the fullscreen flag from the current theme.
7533bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        value = context.findItemInStyle(currentTheme, "windowFullscreen");
7543bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
7553bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // because it may reference something else, we resolve it.
7563bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        value = context.resolveResValue(value);
7573bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
7583bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (value == null || value.getValue() == null ||
7593bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                XmlUtils.convertValueToBoolean(value.getValue(), false /* defValue */) == false) {
7603bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
7613bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // default value
7623bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            int defaultOffset = DEFAULT_STATUS_BAR_HEIGHT;
7633bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
7643bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // get the real value, first the list of Dimensions from the framework map
7653bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            Map<String, IResourceValue> dimens = frameworkResources.get(BridgeConstants.RES_DIMEN);
7663bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
7673bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // now get the value
7683bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            value = dimens.get("status_bar_height");
7693bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            if (value != null) {
7703bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                TypedValue typedValue = ResourceHelper.getValue(value.getValue());
7713bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                if (typedValue != null) {
7723bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    // compute the pixel value based on the display metrics
7733bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    defaultOffset = (int)typedValue.getDimension(context.getResources().mMetrics);
7743bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                }
7753bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            }
7763bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
7773bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // add the computed offset.
7783bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            offset += defaultOffset;
7793bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
7803bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
7813bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        return offset;
7823bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
7833bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
7843bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
7853bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    /**
7863bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * Post process on a view hierachy that was just inflated.
7873bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * <p/>At the moment this only support TabHost: If {@link TabHost} is detected, look for the
7883bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * {@link TabWidget}, and the corresponding {@link FrameLayout} and make new tabs automatically
7893bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * based on the content of the {@link FrameLayout}.
7903bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param view the root view to process.
7913bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param projectCallback callback to the project.
7923bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     */
7933bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private void postInflateProcess(View view, IProjectCallback projectCallback)
7943bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            throws PostInflateException {
7953bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (view instanceof TabHost) {
7963bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            setupTabHost((TabHost)view, projectCallback);
7973bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        } else if (view instanceof ViewGroup) {
7983bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            ViewGroup group = (ViewGroup)view;
7993bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            final int count = group.getChildCount();
8003bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            for (int c = 0 ; c < count ; c++) {
8013bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                View child = group.getChildAt(c);
8023bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                postInflateProcess(child, projectCallback);
8033bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            }
8043bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
8053bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
8063bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
8073bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    /**
8083bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * Sets up a {@link TabHost} object.
8093bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param tabHost the TabHost to setup.
8103bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param projectCallback The project callback object to access the project R class.
8113bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @throws PostInflateException
8123bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     */
8133bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private void setupTabHost(TabHost tabHost, IProjectCallback projectCallback)
8143bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            throws PostInflateException {
8153bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // look for the TabWidget, and the FrameLayout. They have their own specific names
8163bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        View v = tabHost.findViewById(android.R.id.tabs);
8173bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
8183bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (v == null) {
8193bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            throw new PostInflateException(
8203bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    "TabHost requires a TabWidget with id \"android:id/tabs\".\n");
8213bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
8223bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
8233bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if ((v instanceof TabWidget) == false) {
8243bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            throw new PostInflateException(String.format(
8253bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    "TabHost requires a TabWidget with id \"android:id/tabs\".\n" +
8263bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    "View found with id 'tabs' is '%s'", v.getClass().getCanonicalName()));
8273bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
8283bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
8293bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        v = tabHost.findViewById(android.R.id.tabcontent);
8303bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
8313bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (v == null) {
8323bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // TODO: see if we can fake tabs even without the FrameLayout (same below when the framelayout is empty)
8333bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            throw new PostInflateException(
8343bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    "TabHost requires a FrameLayout with id \"android:id/tabcontent\".");
8353bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
8363bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
8373bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if ((v instanceof FrameLayout) == false) {
8383bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            throw new PostInflateException(String.format(
8393bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    "TabHost requires a FrameLayout with id \"android:id/tabcontent\".\n" +
8403bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    "View found with id 'tabcontent' is '%s'", v.getClass().getCanonicalName()));
8413bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
8423bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
8433bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        FrameLayout content = (FrameLayout)v;
8443bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
8453bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // now process the content of the framelayout and dynamically create tabs for it.
8463bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        final int count = content.getChildCount();
8473bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
8483bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (count == 0) {
8493bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            throw new PostInflateException(
8503bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    "The FrameLayout for the TabHost has no content. Rendering failed.\n");
8513bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
8523bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
8533bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // this must be called before addTab() so that the TabHost searches its TabWidget
8543bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // and FrameLayout.
8553bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        tabHost.setup();
8563bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
8573bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // for each child of the framelayout, add a new TabSpec
8583bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        for (int i = 0 ; i < count ; i++) {
8593bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            View child = content.getChildAt(i);
8603bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            String tabSpec = String.format("tab_spec%d", i+1);
8613bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            int id = child.getId();
8623bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            String[] resource = projectCallback.resolveResourceValue(id);
8633bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            String name;
8643bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            if (resource != null) {
8653bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                name = resource[0]; // 0 is resource name, 1 is resource type.
8663bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            } else {
8673bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                name = String.format("Tab %d", i+1); // default name if id is unresolved.
8683bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            }
8693bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            tabHost.addTab(tabHost.newTabSpec(tabSpec).setIndicator(name).setContent(id));
8703bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
8713bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
8723bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
8733bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
8743bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    /**
8753bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * Visits a View and its children and generate a {@link ViewInfo} containing the
8763bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * bounds of all the views.
8773bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param view the root View
8783bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param context the context.
8793bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     */
8803bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private ViewInfo visit(View view, BridgeContext context) {
8813bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (view == null) {
8823bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            return null;
8833bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
8843bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
8853bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        ViewInfo result = new ViewInfo(view.getClass().getName(),
8863bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                context.getViewKey(view),
8872d240967c9bc38cbf69967457b33f953f8826e96Xavier Ducrohet                view.getLeft(), view.getTop(), view.getRight(), view.getBottom(),
8882d240967c9bc38cbf69967457b33f953f8826e96Xavier Ducrohet                view, view.getLayoutParams());
8893bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
8903bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (view instanceof ViewGroup) {
8913bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            ViewGroup group = ((ViewGroup) view);
8923bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            List<ViewInfo> children = new ArrayList<ViewInfo>();
8933bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            for (int i = 0; i < group.getChildCount(); i++) {
8943bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                children.add(visit(group.getChildAt(i), context));
8953bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            }
8963bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            result.setChildren(children);
8973bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
8983bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
8993bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        return result;
9003bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
9013bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
9023bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    public BufferedImage getImage() {
9033bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        return mImage;
9043bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
9053bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
9063bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    public ViewInfo getViewInfo() {
9073bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        return mViewInfo;
9083bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
9092d240967c9bc38cbf69967457b33f953f8826e96Xavier Ducrohet
9102d240967c9bc38cbf69967457b33f953f8826e96Xavier Ducrohet    public Map<String, String> getDefaultViewPropertyValues(Object viewObject) {
9112d240967c9bc38cbf69967457b33f953f8826e96Xavier Ducrohet        return mContext.getDefaultPropMap(viewObject);
9122d240967c9bc38cbf69967457b33f953f8826e96Xavier Ducrohet    }
9133bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet}
914