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
197062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohetimport com.android.ide.common.rendering.api.AdapterBinding;
2064f5dc0e6437e95695ed4048f047c3cbef447e0fXavier Ducrohetimport com.android.ide.common.rendering.api.HardwareConfig;
215cee4833e93b8f9aa9f38815eaa342fab9cc8658Diego Perezimport com.android.ide.common.rendering.api.LayoutLog;
22b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Guptaimport com.android.ide.common.rendering.api.LayoutlibCallback;
230d829bd739dfcb3374971c776676bf2e53bab43cXavier Ducrohetimport com.android.ide.common.rendering.api.RenderResources;
24ecb67b629266bd616da571c1e391164a4f8c8e93Xavier Ducrohetimport com.android.ide.common.rendering.api.RenderSession;
257062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohetimport com.android.ide.common.rendering.api.ResourceReference;
26ecb67b629266bd616da571c1e391164a4f8c8e93Xavier Ducrohetimport com.android.ide.common.rendering.api.ResourceValue;
27ecb67b629266bd616da571c1e391164a4f8c8e93Xavier Ducrohetimport com.android.ide.common.rendering.api.Result;
28d98e133b3911b8db3430ce9d85efd5a6adcf70bfXavier Ducrohetimport com.android.ide.common.rendering.api.SessionParams;
29b32414fe256a6fe1d65e7a1443cd6ea77db8bd95Xavier Ducrohetimport com.android.ide.common.rendering.api.SessionParams.RenderingMode;
30d98e133b3911b8db3430ce9d85efd5a6adcf70bfXavier Ducrohetimport com.android.ide.common.rendering.api.ViewInfo;
3170114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Guptaimport com.android.ide.common.rendering.api.ViewType;
3241670df819e35f5822cc166db775ed2e9e3dfb7dDeepanshu Guptaimport com.android.internal.view.menu.ActionMenuItemView;
3341670df819e35f5822cc166db775ed2e9e3dfb7dDeepanshu Guptaimport com.android.internal.view.menu.BridgeMenuItemImpl;
3441670df819e35f5822cc166db775ed2e9e3dfb7dDeepanshu Guptaimport com.android.internal.view.menu.IconMenuItemView;
3541670df819e35f5822cc166db775ed2e9e3dfb7dDeepanshu Guptaimport com.android.internal.view.menu.ListMenuItemView;
3641670df819e35f5822cc166db775ed2e9e3dfb7dDeepanshu Guptaimport com.android.internal.view.menu.MenuItemImpl;
3741670df819e35f5822cc166db775ed2e9e3dfb7dDeepanshu Guptaimport com.android.internal.view.menu.MenuView;
38ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohetimport com.android.layoutlib.bridge.Bridge;
393bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport com.android.layoutlib.bridge.android.BridgeContext;
403bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
41a4a2d3d5936bcdda1409796179725d354e5f4400Andrew Shulaevimport com.android.layoutlib.bridge.android.RenderParamsFlags;
42259504c6368e44adfffd4f56f7a838b2d5e73ff9Diego Perezimport com.android.layoutlib.bridge.android.graphics.NopCanvas;
43c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsenimport com.android.layoutlib.bridge.android.support.DesignLibUtil;
44f59abd4701479e496d955cb8521a813e535aefa3Jerome Gaillardimport com.android.layoutlib.bridge.android.support.FragmentTabHostUtil;
45b321cbfeb00723a6d18746f0d5bbcfd6b6602f6eDiego Perezimport com.android.layoutlib.bridge.android.support.SupportPreferencesUtil;
467062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohetimport com.android.layoutlib.bridge.impl.binding.FakeAdapter;
477062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohetimport com.android.layoutlib.bridge.impl.binding.FakeExpandableAdapter;
48f59abd4701479e496d955cb8521a813e535aefa3Jerome Gaillardimport com.android.layoutlib.bridge.util.ReflectionUtils;
491b5ef2d19e1d7cb491c1c79f867e38ec0bde2770Xavier Ducrohetimport com.android.resources.ResourceType;
50344cbc9b8ed3ae06a5b183d684df0399fef59879Deepanshu Guptaimport com.android.tools.layoutlib.java.System_Delegate;
51947b979a91ac1cd556244d875d8b8d4fec8fd474Xavier Ducrohetimport com.android.util.Pair;
5218be29a3d14f1b3e840e8ee88b5a1951a6f6e9c8Deepanshu Guptaimport com.android.util.PropertiesMap;
533bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
54442aee6bc1abfb143dcfa1ba60d696e576d066c4Deepanshu Guptaimport android.annotation.NonNull;
55442aee6bc1abfb143dcfa1ba60d696e576d066c4Deepanshu Guptaimport android.annotation.Nullable;
563bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.app.Fragment_Delegate;
573bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.graphics.Bitmap;
583bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.graphics.Bitmap_Delegate;
593bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.graphics.Canvas;
60e4cf18face6980b0c7ac42761512cd93b89927c3Diego Perezimport android.os.Looper;
618a16d5d203a48e7b13761f329333b3dcb8f8210bDeepanshu Guptaimport android.preference.Preference_Delegate;
62994236e95e9809ec8c1d06a218f606f1af2083afXavier Ducrohetimport android.view.AttachInfo_Accessor;
63994236e95e9809ec8c1d06a218f606f1af2083afXavier Ducrohetimport android.view.BridgeInflater;
64344cbc9b8ed3ae06a5b183d684df0399fef59879Deepanshu Guptaimport android.view.Choreographer_Delegate;
653bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.view.View;
663bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.view.View.MeasureSpec;
67d98e133b3911b8db3430ce9d85efd5a6adcf70bfXavier Ducrohetimport android.view.ViewGroup;
68ce63b05275ddd2acd9d2f39e9ab3cb3039f26677Xavier Ducrohetimport android.view.ViewGroup.LayoutParams;
696208081afd488af51d4f7cbf858e2de59d47e4e5Xavier Ducrohetimport android.view.ViewGroup.MarginLayoutParams;
7070114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Guptaimport android.view.ViewParent;
717062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohetimport android.widget.AbsListView;
727062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohetimport android.widget.AbsSpinner;
7370114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Guptaimport android.widget.ActionMenuView;
747062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohetimport android.widget.AdapterView;
757062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohetimport android.widget.ExpandableListView;
763bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.widget.FrameLayout;
77f14c342b8a0024e5ca8c2c3ffce8b79abe03e941Tor Norbyeimport android.widget.LinearLayout;
787062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohetimport android.widget.ListView;
7962c9c3463a9b4a4bc899f5db353f2856876e6590Xavier Ducrohetimport android.widget.QuickContactBadge;
803bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport android.widget.TabHost;
81f14c342b8a0024e5ca8c2c3ffce8b79abe03e941Tor Norbyeimport android.widget.TabHost.TabSpec;
82d98e133b3911b8db3430ce9d85efd5a6adcf70bfXavier Ducrohetimport android.widget.TabWidget;
833bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
844c6c050f68c67097b92eb38e9f01dae9885907caXavier Ducrohetimport java.awt.AlphaComposite;
853bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport java.awt.Color;
863bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport java.awt.Graphics2D;
873bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport java.awt.image.BufferedImage;
883bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport java.util.ArrayList;
893bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport java.util.List;
903bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohetimport java.util.Map;
913bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
92b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Guptaimport static com.android.ide.common.rendering.api.Result.Status.ERROR_INFLATION;
93b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Guptaimport static com.android.ide.common.rendering.api.Result.Status.ERROR_NOT_INFLATED;
94b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Guptaimport static com.android.ide.common.rendering.api.Result.Status.ERROR_UNKNOWN;
95b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Guptaimport static com.android.ide.common.rendering.api.Result.Status.SUCCESS;
96baf88de1f5c435a788f6c38720354b2dbaa19e60Deepanshu Guptaimport static com.android.layoutlib.bridge.util.ReflectionUtils.isInstanceOf;
97b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Gupta
983bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet/**
99ecb67b629266bd616da571c1e391164a4f8c8e93Xavier Ducrohet * Class implementing the render session.
100952c989b307d4b710ee1adba064f663808af9eb3Deepanshu Gupta * <p/>
101ecb67b629266bd616da571c1e391164a4f8c8e93Xavier Ducrohet * A session is a stateful representation of a layout file. It is initialized with data coming
102ecb67b629266bd616da571c1e391164a4f8c8e93Xavier Ducrohet * through the {@link Bridge} API to inflate the layout. Further actions and rendering can then
1033bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet * be done on the layout.
1043bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet */
1050fde9af6be75b32a401eafc8904426424e00e70cXavier Ducrohetpublic class RenderSessionImpl extends RenderAction<SessionParams> {
1063bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
107259504c6368e44adfffd4f56f7a838b2d5e73ff9Diego Perez    private static final Canvas NOP_CANVAS = new NopCanvas();
108259504c6368e44adfffd4f56f7a838b2d5e73ff9Diego Perez
1093bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    // scene state
110ecb67b629266bd616da571c1e391164a4f8c8e93Xavier Ducrohet    private RenderSession mScene;
1113bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private BridgeXmlBlockParser mBlockParser;
1123bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private BridgeInflater mInflater;
113799fe3b371c812a0e94872f896b54afa44e94868Xavier Ducrohet    private ViewGroup mViewRoot;
114799fe3b371c812a0e94872f896b54afa44e94868Xavier Ducrohet    private FrameLayout mContentRoot;
1151392615c49943383baf51262e2df6975266b4c2fXavier Ducrohet    private Canvas mCanvas;
1161392615c49943383baf51262e2df6975266b4c2fXavier Ducrohet    private int mMeasuredScreenWidth = -1;
1171392615c49943383baf51262e2df6975266b4c2fXavier Ducrohet    private int mMeasuredScreenHeight = -1;
118799fe3b371c812a0e94872f896b54afa44e94868Xavier Ducrohet    private boolean mIsAlphaChannelImage;
119344cbc9b8ed3ae06a5b183d684df0399fef59879Deepanshu Gupta    /** If >= 0, a frame will be executed */
120344cbc9b8ed3ae06a5b183d684df0399fef59879Deepanshu Gupta    private long mElapsedFrameTimeNanos = -1;
121344cbc9b8ed3ae06a5b183d684df0399fef59879Deepanshu Gupta    /** True if one frame has been already executed to start the animations */
122344cbc9b8ed3ae06a5b183d684df0399fef59879Deepanshu Gupta    private boolean mFirstFrameExecuted = false;
123a6a38c02bfd3f6a678ac1671f5744b8e439e075aXavier Ducrohet
1243bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    // information being returned through the API
1253bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private BufferedImage mImage;
126e9a2ea4ac32589b372ec195d8be59391c686a8c6Xavier Ducrohet    private List<ViewInfo> mViewInfoList;
12785c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta    private List<ViewInfo> mSystemViewInfoList;
1288b4b0ce31acc9f57c7df5b1c70183a6655c1edfbDeepanshu Gupta    private Layout.Builder mLayoutBuilder;
1296e1b7e3e383e12d36356841130647b846e4c8467Diego Perez    private boolean mNewRenderSize;
1303bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
1313bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    private static final class PostInflateException extends Exception {
1323bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        private static final long serialVersionUID = 1L;
1333bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
134bca9e5623c820c9aa1e959e3874442de9a9a6c32Diego Perez        private PostInflateException(String message) {
1353bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            super(message);
1363bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
1373bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
1383bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
1393bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    /**
1403bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * Creates a layout scene with all the information coming from the layout bridge API.
1413bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * <p>
1428d43509cc2c0f95bd07d6585dadfac296bc5d24dDeepanshu Gupta     * This <b>must</b> be followed by a call to {@link RenderSessionImpl#init(long)},
1438d43509cc2c0f95bd07d6585dadfac296bc5d24dDeepanshu Gupta     * which act as a
144ecb67b629266bd616da571c1e391164a4f8c8e93Xavier Ducrohet     * call to {@link RenderSessionImpl#acquire(long)}
1453bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     *
1468d43509cc2c0f95bd07d6585dadfac296bc5d24dDeepanshu Gupta     * @see Bridge#createSession(SessionParams)
1473bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     */
148b32414fe256a6fe1d65e7a1443cd6ea77db8bd95Xavier Ducrohet    public RenderSessionImpl(SessionParams params) {
1490fde9af6be75b32a401eafc8904426424e00e70cXavier Ducrohet        super(new SessionParams(params));
150ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet    }
151ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet
152ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet    /**
153ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * Initializes and acquires the scene, creating various Android objects such as context,
154ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * inflater, and parser.
155ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     *
156ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * @param timeout the time to wait if another rendering is happening.
157ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     *
158ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * @return whether the scene was prepared
159ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     *
160ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * @see #acquire(long)
161ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * @see #release()
162ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     */
1630fde9af6be75b32a401eafc8904426424e00e70cXavier Ducrohet    @Override
164ecb67b629266bd616da571c1e391164a4f8c8e93Xavier Ducrohet    public Result init(long timeout) {
1650fde9af6be75b32a401eafc8904426424e00e70cXavier Ducrohet        Result result = super.init(timeout);
166952c989b307d4b710ee1adba064f663808af9eb3Deepanshu Gupta        if (!result.isSuccess()) {
167ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet            return result;
168ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        }
1693bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
1700fde9af6be75b32a401eafc8904426424e00e70cXavier Ducrohet        SessionParams params = getParams();
1710fde9af6be75b32a401eafc8904426424e00e70cXavier Ducrohet        BridgeContext context = getContext();
1723bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
173a6a38c02bfd3f6a678ac1671f5744b8e439e075aXavier Ducrohet        // use default of true in case it's not found to use alpha by default
174300f4893351a9974fa3380c513b29d4a7883cd74Deepanshu Gupta        mIsAlphaChannelImage = ResourceHelper.getBooleanThemeValue(params.getResources(),
175300f4893351a9974fa3380c513b29d4a7883cd74Deepanshu Gupta                "windowIsFloating", true, true);
176ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet
1778b4b0ce31acc9f57c7df5b1c70183a6655c1edfbDeepanshu Gupta        mLayoutBuilder = new Layout.Builder(params, context);
1783bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
1793bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // build the inflater and parser.
180b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Gupta        mInflater = new BridgeInflater(context, params.getLayoutlibCallback());
1810fde9af6be75b32a401eafc8904426424e00e70cXavier Ducrohet        context.setBridgeInflater(mInflater);
182ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet
1838b4b0ce31acc9f57c7df5b1c70183a6655c1edfbDeepanshu Gupta        mBlockParser = new BridgeXmlBlockParser(params.getLayoutDescription(), context, false);
184ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet
1853054fe698a6be732e24128541a1db867ec606a59Xavier Ducrohet        return SUCCESS.createResult();
1863bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
1873bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
1883bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    /**
1896e1b7e3e383e12d36356841130647b846e4c8467Diego Perez     * Measures the the current layout if needed (see {@link #invalidateRenderingSize}).
1906e1b7e3e383e12d36356841130647b846e4c8467Diego Perez     */
1917bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez    private void measureLayout(@NonNull SessionParams params) {
1926e1b7e3e383e12d36356841130647b846e4c8467Diego Perez        // only do the screen measure when needed.
1936e1b7e3e383e12d36356841130647b846e4c8467Diego Perez        if (mMeasuredScreenWidth != -1) {
1946e1b7e3e383e12d36356841130647b846e4c8467Diego Perez            return;
1956e1b7e3e383e12d36356841130647b846e4c8467Diego Perez        }
1966e1b7e3e383e12d36356841130647b846e4c8467Diego Perez
1976e1b7e3e383e12d36356841130647b846e4c8467Diego Perez        RenderingMode renderingMode = params.getRenderingMode();
1986e1b7e3e383e12d36356841130647b846e4c8467Diego Perez        HardwareConfig hardwareConfig = params.getHardwareConfig();
1996e1b7e3e383e12d36356841130647b846e4c8467Diego Perez
2006e1b7e3e383e12d36356841130647b846e4c8467Diego Perez        mNewRenderSize = true;
2016e1b7e3e383e12d36356841130647b846e4c8467Diego Perez        mMeasuredScreenWidth = hardwareConfig.getScreenWidth();
2026e1b7e3e383e12d36356841130647b846e4c8467Diego Perez        mMeasuredScreenHeight = hardwareConfig.getScreenHeight();
2036e1b7e3e383e12d36356841130647b846e4c8467Diego Perez
2046e1b7e3e383e12d36356841130647b846e4c8467Diego Perez        if (renderingMode != RenderingMode.NORMAL) {
2056e1b7e3e383e12d36356841130647b846e4c8467Diego Perez            int widthMeasureSpecMode = renderingMode.isHorizExpand() ?
2066e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                    MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
2076e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                    : MeasureSpec.EXACTLY;
2086e1b7e3e383e12d36356841130647b846e4c8467Diego Perez            int heightMeasureSpecMode = renderingMode.isVertExpand() ?
2096e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                    MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
2106e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                    : MeasureSpec.EXACTLY;
2116e1b7e3e383e12d36356841130647b846e4c8467Diego Perez
2126e1b7e3e383e12d36356841130647b846e4c8467Diego Perez            // We used to compare the measured size of the content to the screen size but
2136e1b7e3e383e12d36356841130647b846e4c8467Diego Perez            // this does not work anymore due to the 2 following issues:
2146e1b7e3e383e12d36356841130647b846e4c8467Diego Perez            // - If the content is in a decor (system bar, title/action bar), the root view
2156e1b7e3e383e12d36356841130647b846e4c8467Diego Perez            //   will not resize even with the UNSPECIFIED because of the embedded layout.
2166e1b7e3e383e12d36356841130647b846e4c8467Diego Perez            // - If there is no decor, but a dialog frame, then the dialog padding prevents
2176e1b7e3e383e12d36356841130647b846e4c8467Diego Perez            //   comparing the size of the content to the screen frame (as it would not
2186e1b7e3e383e12d36356841130647b846e4c8467Diego Perez            //   take into account the dialog padding).
2196e1b7e3e383e12d36356841130647b846e4c8467Diego Perez
2206e1b7e3e383e12d36356841130647b846e4c8467Diego Perez            // The solution is to first get the content size in a normal rendering, inside
2216e1b7e3e383e12d36356841130647b846e4c8467Diego Perez            // the decor or the dialog padding.
2226e1b7e3e383e12d36356841130647b846e4c8467Diego Perez            // Then measure only the content with UNSPECIFIED to see the size difference
2236e1b7e3e383e12d36356841130647b846e4c8467Diego Perez            // and apply this to the screen size.
2246e1b7e3e383e12d36356841130647b846e4c8467Diego Perez
225bca9e5623c820c9aa1e959e3874442de9a9a6c32Diego Perez            View measuredView = mContentRoot.getChildAt(0);
226bca9e5623c820c9aa1e959e3874442de9a9a6c32Diego Perez
2276e1b7e3e383e12d36356841130647b846e4c8467Diego Perez            // first measure the full layout, with EXACTLY to get the size of the
2286e1b7e3e383e12d36356841130647b846e4c8467Diego Perez            // content as it is inside the decor/dialog
2296e1b7e3e383e12d36356841130647b846e4c8467Diego Perez            @SuppressWarnings("deprecation")
2306e1b7e3e383e12d36356841130647b846e4c8467Diego Perez            Pair<Integer, Integer> exactMeasure = measureView(
231bca9e5623c820c9aa1e959e3874442de9a9a6c32Diego Perez                    mViewRoot, measuredView,
2326e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                    mMeasuredScreenWidth, MeasureSpec.EXACTLY,
2336e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                    mMeasuredScreenHeight, MeasureSpec.EXACTLY);
2346e1b7e3e383e12d36356841130647b846e4c8467Diego Perez
2356e1b7e3e383e12d36356841130647b846e4c8467Diego Perez            // now measure the content only using UNSPECIFIED (where applicable, based on
2366e1b7e3e383e12d36356841130647b846e4c8467Diego Perez            // the rendering mode). This will give us the size the content needs.
2376e1b7e3e383e12d36356841130647b846e4c8467Diego Perez            @SuppressWarnings("deprecation")
2386e1b7e3e383e12d36356841130647b846e4c8467Diego Perez            Pair<Integer, Integer> result = measureView(
2396e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                    mContentRoot, mContentRoot.getChildAt(0),
2406e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                    mMeasuredScreenWidth, widthMeasureSpecMode,
2416e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                    mMeasuredScreenHeight, heightMeasureSpecMode);
2426e1b7e3e383e12d36356841130647b846e4c8467Diego Perez
243bca9e5623c820c9aa1e959e3874442de9a9a6c32Diego Perez            // If measuredView is not null, exactMeasure nor result will be null.
244bca9e5623c820c9aa1e959e3874442de9a9a6c32Diego Perez            assert exactMeasure != null;
245bca9e5623c820c9aa1e959e3874442de9a9a6c32Diego Perez            assert result != null;
246bca9e5623c820c9aa1e959e3874442de9a9a6c32Diego Perez
2476e1b7e3e383e12d36356841130647b846e4c8467Diego Perez            // now look at the difference and add what is needed.
2486e1b7e3e383e12d36356841130647b846e4c8467Diego Perez            if (renderingMode.isHorizExpand()) {
2496e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                int measuredWidth = exactMeasure.getFirst();
2506e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                int neededWidth = result.getFirst();
2516e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                if (neededWidth > measuredWidth) {
2526e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                    mMeasuredScreenWidth += neededWidth - measuredWidth;
2536e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                }
2546e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                if (mMeasuredScreenWidth < measuredWidth) {
2556e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                    // If the screen width is less than the exact measured width,
2566e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                    // expand to match.
2576e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                    mMeasuredScreenWidth = measuredWidth;
2586e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                }
2596e1b7e3e383e12d36356841130647b846e4c8467Diego Perez            }
2606e1b7e3e383e12d36356841130647b846e4c8467Diego Perez
2616e1b7e3e383e12d36356841130647b846e4c8467Diego Perez            if (renderingMode.isVertExpand()) {
2626e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                int measuredHeight = exactMeasure.getSecond();
2636e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                int neededHeight = result.getSecond();
2646e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                if (neededHeight > measuredHeight) {
2656e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                    mMeasuredScreenHeight += neededHeight - measuredHeight;
2666e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                }
2676e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                if (mMeasuredScreenHeight < measuredHeight) {
2686e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                    // If the screen height is less than the exact measured height,
2696e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                    // expand to match.
2706e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                    mMeasuredScreenHeight = measuredHeight;
2716e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                }
2726e1b7e3e383e12d36356841130647b846e4c8467Diego Perez            }
2736e1b7e3e383e12d36356841130647b846e4c8467Diego Perez        }
2746e1b7e3e383e12d36356841130647b846e4c8467Diego Perez    }
2756e1b7e3e383e12d36356841130647b846e4c8467Diego Perez
2766e1b7e3e383e12d36356841130647b846e4c8467Diego Perez    /**
2773bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * Inflates the layout.
2783bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * <p>
279ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * {@link #acquire(long)} must have been called before this.
280ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     *
281ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * @throws IllegalStateException if the current context is different than the one owned by
282ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     *      the scene, or if {@link #init(long)} was not called.
2833bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     */
284ecb67b629266bd616da571c1e391164a4f8c8e93Xavier Ducrohet    public Result inflate() {
285ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        checkLock();
286ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet
2873bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        try {
2888b4b0ce31acc9f57c7df5b1c70183a6655c1edfbDeepanshu Gupta            mViewRoot = new Layout(mLayoutBuilder);
2898b4b0ce31acc9f57c7df5b1c70183a6655c1edfbDeepanshu Gupta            mLayoutBuilder = null;  // Done with the builder.
2908b4b0ce31acc9f57c7df5b1c70183a6655c1edfbDeepanshu Gupta            mContentRoot = ((Layout) mViewRoot).getContentRoot();
2910fde9af6be75b32a401eafc8904426424e00e70cXavier Ducrohet            SessionParams params = getParams();
2920fde9af6be75b32a401eafc8904426424e00e70cXavier Ducrohet            BridgeContext context = getContext();
2933bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
2945cee4833e93b8f9aa9f38815eaa342fab9cc8658Diego Perez            if (Bridge.isLocaleRtl(params.getLocale())) {
2955cee4833e93b8f9aa9f38815eaa342fab9cc8658Diego Perez                if (!params.isRtlSupported()) {
2965cee4833e93b8f9aa9f38815eaa342fab9cc8658Diego Perez                    Bridge.getLog().warning(LayoutLog.TAG_RTL_NOT_ENABLED,
2975cee4833e93b8f9aa9f38815eaa342fab9cc8658Diego Perez                            "You are using a right-to-left " +
2985cee4833e93b8f9aa9f38815eaa342fab9cc8658Diego Perez                                    "(RTL) locale but RTL is not enabled", null);
2995cee4833e93b8f9aa9f38815eaa342fab9cc8658Diego Perez                } else if (params.getSimulatedPlatformVersion() < 17) {
3005cee4833e93b8f9aa9f38815eaa342fab9cc8658Diego Perez                    // This will render ok because we are using the latest layoutlib but at least
3015cee4833e93b8f9aa9f38815eaa342fab9cc8658Diego Perez                    // warn the user that this might fail in a real device.
3025cee4833e93b8f9aa9f38815eaa342fab9cc8658Diego Perez                    Bridge.getLog().warning(LayoutLog.TAG_RTL_NOT_SUPPORTED, "You are using a " +
3035cee4833e93b8f9aa9f38815eaa342fab9cc8658Diego Perez                            "right-to-left " +
3045cee4833e93b8f9aa9f38815eaa342fab9cc8658Diego Perez                            "(RTL) locale but RTL is not supported for API level < 17", null);
3055cee4833e93b8f9aa9f38815eaa342fab9cc8658Diego Perez                }
3065cee4833e93b8f9aa9f38815eaa342fab9cc8658Diego Perez            }
3075cee4833e93b8f9aa9f38815eaa342fab9cc8658Diego Perez
3083bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // Sets the project callback (custom view loader) to the fragment delegate so that
3093bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // it can instantiate the custom Fragment.
310b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Gupta            Fragment_Delegate.setLayoutlibCallback(params.getLayoutlibCallback());
3113bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
312a4a2d3d5936bcdda1409796179725d354e5f4400Andrew Shulaev            String rootTag = params.getFlag(RenderParamsFlags.FLAG_KEY_ROOT_TAG);
3138a16d5d203a48e7b13761f329333b3dcb8f8210bDeepanshu Gupta            boolean isPreference = "PreferenceScreen".equals(rootTag);
3148a16d5d203a48e7b13761f329333b3dcb8f8210bDeepanshu Gupta            View view;
3158a16d5d203a48e7b13761f329333b3dcb8f8210bDeepanshu Gupta            if (isPreference) {
316b321cbfeb00723a6d18746f0d5bbcfd6b6602f6eDiego Perez                // First try to use the support library inflater. If something fails, fallback
317b321cbfeb00723a6d18746f0d5bbcfd6b6602f6eDiego Perez                // to the system preference inflater.
318b321cbfeb00723a6d18746f0d5bbcfd6b6602f6eDiego Perez                view = SupportPreferencesUtil.inflatePreference(getContext(), mBlockParser,
3198b4b0ce31acc9f57c7df5b1c70183a6655c1edfbDeepanshu Gupta                        mContentRoot);
320b321cbfeb00723a6d18746f0d5bbcfd6b6602f6eDiego Perez                if (view == null) {
321b321cbfeb00723a6d18746f0d5bbcfd6b6602f6eDiego Perez                    view = Preference_Delegate.inflatePreference(getContext(), mBlockParser,
322b321cbfeb00723a6d18746f0d5bbcfd6b6602f6eDiego Perez                            mContentRoot);
323b321cbfeb00723a6d18746f0d5bbcfd6b6602f6eDiego Perez                }
3248a16d5d203a48e7b13761f329333b3dcb8f8210bDeepanshu Gupta            } else {
3258a16d5d203a48e7b13761f329333b3dcb8f8210bDeepanshu Gupta                view = mInflater.inflate(mBlockParser, mContentRoot);
3268a16d5d203a48e7b13761f329333b3dcb8f8210bDeepanshu Gupta            }
3273bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
3287062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet            // done with the parser, pop it.
3297062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet            context.popParser();
3307062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet
331b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Gupta            Fragment_Delegate.setLayoutlibCallback(null);
3323bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
3333bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // set the AttachInfo on the root view.
334994236e95e9809ec8c1d06a218f606f1af2083afXavier Ducrohet            AttachInfo_Accessor.setAttachInfo(mViewRoot);
3353bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
336a2378f55b81e7d29f451d7f65527de49417a3f0cXavier Ducrohet            // post-inflate process. For now this supports TabHost/TabWidget
337b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Gupta            postInflateProcess(view, params.getLayoutlibCallback(), isPreference ? view : null);
3389028fa93da0f9c7dad2176de347cd6e705084c9fDeepanshu Gupta            mInflater.onDoneInflation();
339a2378f55b81e7d29f451d7f65527de49417a3f0cXavier Ducrohet
340c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen            setActiveToolbar(view, context, params);
341c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen
3427bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez            measureLayout(params);
3436e1b7e3e383e12d36356841130647b846e4c8467Diego Perez            measureView(mViewRoot, null /*measuredView*/,
3446e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                    mMeasuredScreenWidth, MeasureSpec.EXACTLY,
3456e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                    mMeasuredScreenHeight, MeasureSpec.EXACTLY);
3466e1b7e3e383e12d36356841130647b846e4c8467Diego Perez            mViewRoot.layout(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight);
347e48b02f441a8a90f0a1618ed2116c83441aae3bdDiego Perez            mSystemViewInfoList =
348e48b02f441a8a90f0a1618ed2116c83441aae3bdDiego Perez                    visitAllChildren(mViewRoot, 0, 0, params.getExtendedViewInfoMode(),
3496e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                    false);
3506e1b7e3e383e12d36356841130647b846e4c8467Diego Perez
35167f948f353b652d3373c7bdb0c2a8715e8b4e9f6Diego Perez            Choreographer_Delegate.clearFrames();
35267f948f353b652d3373c7bdb0c2a8715e8b4e9f6Diego Perez
353ecb67b629266bd616da571c1e391164a4f8c8e93Xavier Ducrohet            return SUCCESS.createResult();
3543bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        } catch (PostInflateException e) {
355ecb67b629266bd616da571c1e391164a4f8c8e93Xavier Ducrohet            return ERROR_INFLATION.createResult(e.getMessage(), e);
3563bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        } catch (Throwable e) {
3573bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // get the real cause of the exception.
3583bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            Throwable t = e;
3593bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            while (t.getCause() != null) {
3603bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                t = t.getCause();
3613bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            }
3623bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
363ecb67b629266bd616da571c1e391164a4f8c8e93Xavier Ducrohet            return ERROR_INFLATION.createResult(t.getMessage(), t);
3643bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
3653bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
3663bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
3673bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    /**
368344cbc9b8ed3ae06a5b183d684df0399fef59879Deepanshu Gupta     * Sets the time for which the next frame will be selected. The time is the elapsed time from
369344cbc9b8ed3ae06a5b183d684df0399fef59879Deepanshu Gupta     * the current system nanos time. You
370344cbc9b8ed3ae06a5b183d684df0399fef59879Deepanshu Gupta     */
371344cbc9b8ed3ae06a5b183d684df0399fef59879Deepanshu Gupta    public void setElapsedFrameTimeNanos(long nanos) {
372344cbc9b8ed3ae06a5b183d684df0399fef59879Deepanshu Gupta        mElapsedFrameTimeNanos = nanos;
373344cbc9b8ed3ae06a5b183d684df0399fef59879Deepanshu Gupta    }
374344cbc9b8ed3ae06a5b183d684df0399fef59879Deepanshu Gupta
375344cbc9b8ed3ae06a5b183d684df0399fef59879Deepanshu Gupta    /**
376f1a0a823499fc0a464e18b8eba992b32d14b8bb1Diego Perez     * Runs a layout pass for the given view root
3770685ab35ad78094ceb2d9fb0583e2cadd1968c90Diego Perez     */
378f1a0a823499fc0a464e18b8eba992b32d14b8bb1Diego Perez    private static void doLayout(@NonNull BridgeContext context, @NonNull ViewGroup viewRoot,
379f1a0a823499fc0a464e18b8eba992b32d14b8bb1Diego Perez            int width, int height) {
3800685ab35ad78094ceb2d9fb0583e2cadd1968c90Diego Perez        // measure again with the size we need
3810685ab35ad78094ceb2d9fb0583e2cadd1968c90Diego Perez        // This must always be done before the call to layout
3820685ab35ad78094ceb2d9fb0583e2cadd1968c90Diego Perez        measureView(viewRoot, null /*measuredView*/,
3830685ab35ad78094ceb2d9fb0583e2cadd1968c90Diego Perez                width, MeasureSpec.EXACTLY,
3840685ab35ad78094ceb2d9fb0583e2cadd1968c90Diego Perez                height, MeasureSpec.EXACTLY);
3850685ab35ad78094ceb2d9fb0583e2cadd1968c90Diego Perez
3860685ab35ad78094ceb2d9fb0583e2cadd1968c90Diego Perez        // now do the layout.
3870685ab35ad78094ceb2d9fb0583e2cadd1968c90Diego Perez        viewRoot.layout(0, 0, width, height);
3880685ab35ad78094ceb2d9fb0583e2cadd1968c90Diego Perez        handleScrolling(context, viewRoot);
389f1a0a823499fc0a464e18b8eba992b32d14b8bb1Diego Perez    }
3900685ab35ad78094ceb2d9fb0583e2cadd1968c90Diego Perez
391f1a0a823499fc0a464e18b8eba992b32d14b8bb1Diego Perez    /**
392f1a0a823499fc0a464e18b8eba992b32d14b8bb1Diego Perez     * Renders the given view hierarchy to the passed canvas and returns the result of the render
393f1a0a823499fc0a464e18b8eba992b32d14b8bb1Diego Perez     * operation.
394f1a0a823499fc0a464e18b8eba992b32d14b8bb1Diego Perez     * @param canvas an optional canvas to render the views to. If null, only the measure and
395f1a0a823499fc0a464e18b8eba992b32d14b8bb1Diego Perez     * layout steps will be executed.
396f1a0a823499fc0a464e18b8eba992b32d14b8bb1Diego Perez     */
397bca9e5623c820c9aa1e959e3874442de9a9a6c32Diego Perez    private static Result renderAndBuildResult(@NonNull ViewGroup viewRoot, @Nullable Canvas canvas) {
3980685ab35ad78094ceb2d9fb0583e2cadd1968c90Diego Perez        if (canvas == null) {
3990685ab35ad78094ceb2d9fb0583e2cadd1968c90Diego Perez            return SUCCESS.createResult();
4000685ab35ad78094ceb2d9fb0583e2cadd1968c90Diego Perez        }
4010685ab35ad78094ceb2d9fb0583e2cadd1968c90Diego Perez
4020685ab35ad78094ceb2d9fb0583e2cadd1968c90Diego Perez        AttachInfo_Accessor.dispatchOnPreDraw(viewRoot);
4030685ab35ad78094ceb2d9fb0583e2cadd1968c90Diego Perez        viewRoot.draw(canvas);
4040685ab35ad78094ceb2d9fb0583e2cadd1968c90Diego Perez
4050685ab35ad78094ceb2d9fb0583e2cadd1968c90Diego Perez        return SUCCESS.createResult();
4060685ab35ad78094ceb2d9fb0583e2cadd1968c90Diego Perez    }
4070685ab35ad78094ceb2d9fb0583e2cadd1968c90Diego Perez
4080685ab35ad78094ceb2d9fb0583e2cadd1968c90Diego Perez    /**
4093bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * Renders the scene.
4103bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * <p>
411ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * {@link #acquire(long)} must have been called before this.
412ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     *
4134c6c050f68c67097b92eb38e9f01dae9885907caXavier Ducrohet     * @param freshRender whether the render is a new one and should erase the existing bitmap (in
4144c6c050f68c67097b92eb38e9f01dae9885907caXavier Ducrohet     *      the case where bitmaps are reused). This is typically needed when not playing
4154c6c050f68c67097b92eb38e9f01dae9885907caXavier Ducrohet     *      animations.)
4164c6c050f68c67097b92eb38e9f01dae9885907caXavier Ducrohet     *
417ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     * @throws IllegalStateException if the current context is different than the one owned by
418ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet     *      the scene, or if {@link #acquire(long)} was not called.
4191392615c49943383baf51262e2df6975266b4c2fXavier Ducrohet     *
4208d43509cc2c0f95bd07d6585dadfac296bc5d24dDeepanshu Gupta     * @see SessionParams#getRenderingMode()
421799fe3b371c812a0e94872f896b54afa44e94868Xavier Ducrohet     * @see RenderSession#render(long)
4223bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     */
4234c6c050f68c67097b92eb38e9f01dae9885907caXavier Ducrohet    public Result render(boolean freshRender) {
4247bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez        return renderAndBuildResult(freshRender, false);
4257bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez    }
4267bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez
4277bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez    /**
4287bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez     * Measures the layout
4297bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez     * <p>
4307bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez     * {@link #acquire(long)} must have been called before this.
4317bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez     *
4327bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez     * @throws IllegalStateException if the current context is different than the one owned by
4337bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez     *      the scene, or if {@link #acquire(long)} was not called.
4347bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez     *
4357bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez     * @see SessionParams#getRenderingMode()
4367bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez     * @see RenderSession#render(long)
4377bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez     */
4387bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez    public Result measure() {
4397bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez        return renderAndBuildResult(false, true);
4407bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez    }
4417bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez
4427bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez    /**
4437bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez     * Renders the scene.
4447bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez     * <p>
4457bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez     * {@link #acquire(long)} must have been called before this.
4467bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez     *
4477bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez     * @param freshRender whether the render is a new one and should erase the existing bitmap (in
4487bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez     *      the case where bitmaps are reused). This is typically needed when not playing
4497bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez     *      animations.)
4507bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez     *
4517bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez     * @throws IllegalStateException if the current context is different than the one owned by
4527bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez     *      the scene, or if {@link #acquire(long)} was not called.
4537bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez     *
4547bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez     * @see SessionParams#getRenderingMode()
4557bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez     * @see RenderSession#render(long)
4567bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez     */
4577bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez    private Result renderAndBuildResult(boolean freshRender, boolean onlyMeasure) {
458ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet        checkLock();
459ddea50d03cdda807bbaea54beffd7a341c51f770Xavier Ducrohet
4600fde9af6be75b32a401eafc8904426424e00e70cXavier Ducrohet        SessionParams params = getParams();
4610fde9af6be75b32a401eafc8904426424e00e70cXavier Ducrohet
4623bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        try {
4633bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            if (mViewRoot == null) {
464ecb67b629266bd616da571c1e391164a4f8c8e93Xavier Ducrohet                return ERROR_NOT_INFLATED.createResult();
4653bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            }
4663bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
4677bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez            measureLayout(params);
4683bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
4696e1b7e3e383e12d36356841130647b846e4c8467Diego Perez            HardwareConfig hardwareConfig = params.getHardwareConfig();
4700685ab35ad78094ceb2d9fb0583e2cadd1968c90Diego Perez            Result renderResult = SUCCESS.createResult();
4717bb8850ed2076ebf8442a5fdb68ec9a8f53623e0Diego Perez            if (onlyMeasure) {
472f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet                // delete the canvas and image to reset them on the next full rendering
473f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet                mImage = null;
474f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet                mCanvas = null;
475f1a0a823499fc0a464e18b8eba992b32d14b8bb1Diego Perez                doLayout(getContext(), mViewRoot, mMeasuredScreenWidth, mMeasuredScreenHeight);
476f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet            } else {
477f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet                // draw the views
478f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet                // create the BufferedImage into which the layout will be rendered.
479f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet                boolean newImage = false;
480e12b3f96291cf0e3dc60d9db9ac3d3859325f3d4Diego Perez
481e12b3f96291cf0e3dc60d9db9ac3d3859325f3d4Diego Perez                // When disableBitmapCaching is true, we do not reuse mImage and
482e12b3f96291cf0e3dc60d9db9ac3d3859325f3d4Diego Perez                // we create a new one in every render.
483e12b3f96291cf0e3dc60d9db9ac3d3859325f3d4Diego Perez                // This is useful when mImage is just a wrapper of Graphics2D so
484e12b3f96291cf0e3dc60d9db9ac3d3859325f3d4Diego Perez                // it doesn't get cached.
485e12b3f96291cf0e3dc60d9db9ac3d3859325f3d4Diego Perez                boolean disableBitmapCaching = Boolean.TRUE.equals(params.getFlag(
486a4a2d3d5936bcdda1409796179725d354e5f4400Andrew Shulaev                    RenderParamsFlags.FLAG_KEY_DISABLE_BITMAP_CACHING));
4876e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                if (mNewRenderSize || mCanvas == null || disableBitmapCaching) {
4886e1b7e3e383e12d36356841130647b846e4c8467Diego Perez                    mNewRenderSize = false;
489f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet                    if (params.getImageFactory() != null) {
490f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet                        mImage = params.getImageFactory().getImage(
491f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet                                mMeasuredScreenWidth,
492f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet                                mMeasuredScreenHeight);
493f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet                    } else {
494f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet                        mImage = new BufferedImage(
495f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet                                mMeasuredScreenWidth,
496f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet                                mMeasuredScreenHeight,
497f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet                                BufferedImage.TYPE_INT_ARGB);
498f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet                        newImage = true;
499f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet                    }
500f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet
501f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet                    if (params.isBgColorOverridden()) {
502f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet                        // since we override the content, it's the same as if it was a new image.
503f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet                        newImage = true;
504f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet                        Graphics2D gc = mImage.createGraphics();
505f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet                        gc.setColor(new Color(params.getOverrideBgColor(), true));
506f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet                        gc.setComposite(AlphaComposite.Src);
507f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet                        gc.fillRect(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight);
508f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet                        gc.dispose();
509f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet                    }
510f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet
511f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet                    // create an Android bitmap around the BufferedImage
512f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet                    Bitmap bitmap = Bitmap_Delegate.createBitmap(mImage,
51364f5dc0e6437e95695ed4048f047c3cbef447e0fXavier Ducrohet                            true /*isMutable*/, hardwareConfig.getDensity());
514f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet
515e12b3f96291cf0e3dc60d9db9ac3d3859325f3d4Diego Perez                    if (mCanvas == null) {
516e12b3f96291cf0e3dc60d9db9ac3d3859325f3d4Diego Perez                        // create a Canvas around the Android bitmap
517e12b3f96291cf0e3dc60d9db9ac3d3859325f3d4Diego Perez                        mCanvas = new Canvas(bitmap);
518e12b3f96291cf0e3dc60d9db9ac3d3859325f3d4Diego Perez                    } else {
519e12b3f96291cf0e3dc60d9db9ac3d3859325f3d4Diego Perez                        mCanvas.setBitmap(bitmap);
520e12b3f96291cf0e3dc60d9db9ac3d3859325f3d4Diego Perez                    }
52164f5dc0e6437e95695ed4048f047c3cbef447e0fXavier Ducrohet                    mCanvas.setDensity(hardwareConfig.getDensity().getDpiValue());
5221392615c49943383baf51262e2df6975266b4c2fXavier Ducrohet                }
5233bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
524952c989b307d4b710ee1adba064f663808af9eb3Deepanshu Gupta                if (freshRender && !newImage) {
5251392615c49943383baf51262e2df6975266b4c2fXavier Ducrohet                    Graphics2D gc = mImage.createGraphics();
5264c6c050f68c67097b92eb38e9f01dae9885907caXavier Ducrohet                    gc.setComposite(AlphaComposite.Src);
5273bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
528f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet                    gc.setColor(new Color(0x00000000, true));
529f4978e031c3ad565f02d5a4c65f87ae4477f3613Deepanshu Gupta                    gc.fillRect(0, 0,
530f4978e031c3ad565f02d5a4c65f87ae4477f3613Deepanshu Gupta                            mMeasuredScreenWidth, mMeasuredScreenHeight);
531a6a38c02bfd3f6a678ac1671f5744b8e439e075aXavier Ducrohet
532f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet                    // done
533f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet                    gc.dispose();
534f27575799aab2e552c0491a07b34389e1422fc27Xavier Ducrohet                }
535a6a38c02bfd3f6a678ac1671f5744b8e439e075aXavier Ducrohet
536f1a0a823499fc0a464e18b8eba992b32d14b8bb1Diego Perez                doLayout(getContext(), mViewRoot, mMeasuredScreenWidth, mMeasuredScreenHeight);
537344cbc9b8ed3ae06a5b183d684df0399fef59879Deepanshu Gupta                if (mElapsedFrameTimeNanos >= 0) {
538344cbc9b8ed3ae06a5b183d684df0399fef59879Deepanshu Gupta                    long initialTime = System_Delegate.nanoTime();
539344cbc9b8ed3ae06a5b183d684df0399fef59879Deepanshu Gupta                    if (!mFirstFrameExecuted) {
5400685ab35ad78094ceb2d9fb0583e2cadd1968c90Diego Perez                        // We need to run an initial draw call to initialize the animations
541bca9e5623c820c9aa1e959e3874442de9a9a6c32Diego Perez                        renderAndBuildResult(mViewRoot, NOP_CANVAS);
5420685ab35ad78094ceb2d9fb0583e2cadd1968c90Diego Perez
543344cbc9b8ed3ae06a5b183d684df0399fef59879Deepanshu Gupta                        // The first frame will initialize the animations
544344cbc9b8ed3ae06a5b183d684df0399fef59879Deepanshu Gupta                        Choreographer_Delegate.doFrame(initialTime);
545344cbc9b8ed3ae06a5b183d684df0399fef59879Deepanshu Gupta                        mFirstFrameExecuted = true;
546344cbc9b8ed3ae06a5b183d684df0399fef59879Deepanshu Gupta                    }
547344cbc9b8ed3ae06a5b183d684df0399fef59879Deepanshu Gupta                    // Second frame will move the animations
548344cbc9b8ed3ae06a5b183d684df0399fef59879Deepanshu Gupta                    Choreographer_Delegate.doFrame(initialTime + mElapsedFrameTimeNanos);
549344cbc9b8ed3ae06a5b183d684df0399fef59879Deepanshu Gupta                }
550bca9e5623c820c9aa1e959e3874442de9a9a6c32Diego Perez                renderResult = renderAndBuildResult(mViewRoot, mCanvas);
5514c6c050f68c67097b92eb38e9f01dae9885907caXavier Ducrohet            }
5524c6c050f68c67097b92eb38e9f01dae9885907caXavier Ducrohet
553e48b02f441a8a90f0a1618ed2116c83441aae3bdDiego Perez            mSystemViewInfoList =
554e48b02f441a8a90f0a1618ed2116c83441aae3bdDiego Perez                    visitAllChildren(mViewRoot, 0, 0, params.getExtendedViewInfoMode(),
55541670df819e35f5822cc166db775ed2e9e3dfb7dDeepanshu Gupta                    false);
5563bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
5573bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // success!
5580685ab35ad78094ceb2d9fb0583e2cadd1968c90Diego Perez            return renderResult;
5593bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        } catch (Throwable e) {
5603bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            // get the real cause of the exception.
5613bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            Throwable t = e;
5623bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            while (t.getCause() != null) {
5633bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                t = t.getCause();
5643bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            }
5653bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
566ecb67b629266bd616da571c1e391164a4f8c8e93Xavier Ducrohet            return ERROR_UNKNOWN.createResult(t.getMessage(), t);
5673bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
5683bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
5693bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
5703bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    /**
5718ae691c1148038995d1185716f629b82c582897fXavier Ducrohet     * Executes {@link View#measure(int, int)} on a given view with the given parameters (used
5728ae691c1148038995d1185716f629b82c582897fXavier Ducrohet     * to create measure specs with {@link MeasureSpec#makeMeasureSpec(int, int)}.
5738ae691c1148038995d1185716f629b82c582897fXavier Ducrohet     *
5748ae691c1148038995d1185716f629b82c582897fXavier Ducrohet     * if <var>measuredView</var> is non null, the method returns a {@link Pair} of (width, height)
5758ae691c1148038995d1185716f629b82c582897fXavier Ducrohet     * for the view (using {@link View#getMeasuredWidth()} and {@link View#getMeasuredHeight()}).
5768ae691c1148038995d1185716f629b82c582897fXavier Ducrohet     *
5778ae691c1148038995d1185716f629b82c582897fXavier Ducrohet     * @param viewToMeasure the view on which to execute measure().
5788ae691c1148038995d1185716f629b82c582897fXavier Ducrohet     * @param measuredView if non null, the view to query for its measured width/height.
5798ae691c1148038995d1185716f629b82c582897fXavier Ducrohet     * @param width the width to use in the MeasureSpec.
5808ae691c1148038995d1185716f629b82c582897fXavier Ducrohet     * @param widthMode the MeasureSpec mode to use for the width.
5818ae691c1148038995d1185716f629b82c582897fXavier Ducrohet     * @param height the height to use in the MeasureSpec.
5828ae691c1148038995d1185716f629b82c582897fXavier Ducrohet     * @param heightMode the MeasureSpec mode to use for the height.
5838ae691c1148038995d1185716f629b82c582897fXavier Ducrohet     * @return the measured width/height if measuredView is non-null, null otherwise.
5848ae691c1148038995d1185716f629b82c582897fXavier Ducrohet     */
585952c989b307d4b710ee1adba064f663808af9eb3Deepanshu Gupta    @SuppressWarnings("deprecation")  // For the use of Pair
5860685ab35ad78094ceb2d9fb0583e2cadd1968c90Diego Perez    private static Pair<Integer, Integer> measureView(ViewGroup viewToMeasure, View measuredView,
5878ae691c1148038995d1185716f629b82c582897fXavier Ducrohet            int width, int widthMode, int height, int heightMode) {
5888ae691c1148038995d1185716f629b82c582897fXavier Ducrohet        int w_spec = MeasureSpec.makeMeasureSpec(width, widthMode);
5898ae691c1148038995d1185716f629b82c582897fXavier Ducrohet        int h_spec = MeasureSpec.makeMeasureSpec(height, heightMode);
5908ae691c1148038995d1185716f629b82c582897fXavier Ducrohet        viewToMeasure.measure(w_spec, h_spec);
5918ae691c1148038995d1185716f629b82c582897fXavier Ducrohet
5928ae691c1148038995d1185716f629b82c582897fXavier Ducrohet        if (measuredView != null) {
5938ae691c1148038995d1185716f629b82c582897fXavier Ducrohet            return Pair.of(measuredView.getMeasuredWidth(), measuredView.getMeasuredHeight());
5948ae691c1148038995d1185716f629b82c582897fXavier Ducrohet        }
5958ae691c1148038995d1185716f629b82c582897fXavier Ducrohet
5968ae691c1148038995d1185716f629b82c582897fXavier Ducrohet        return null;
5978ae691c1148038995d1185716f629b82c582897fXavier Ducrohet    }
5988ae691c1148038995d1185716f629b82c582897fXavier Ducrohet
5998ae691c1148038995d1185716f629b82c582897fXavier Ducrohet    /**
600952c989b307d4b710ee1adba064f663808af9eb3Deepanshu Gupta     * Post process on a view hierarchy that was just inflated.
601952c989b307d4b710ee1adba064f663808af9eb3Deepanshu Gupta     * <p/>
602952c989b307d4b710ee1adba064f663808af9eb3Deepanshu Gupta     * At the moment this only supports TabHost: If {@link TabHost} is detected, look for the
6033bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * {@link TabWidget}, and the corresponding {@link FrameLayout} and make new tabs automatically
6043bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * based on the content of the {@link FrameLayout}.
6053bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param view the root view to process.
606b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Gupta     * @param layoutlibCallback callback to the project.
6078a16d5d203a48e7b13761f329333b3dcb8f8210bDeepanshu Gupta     * @param skip the view and it's children are not processed.
6083bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     */
609952c989b307d4b710ee1adba064f663808af9eb3Deepanshu Gupta    @SuppressWarnings("deprecation")  // For the use of Pair
610b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Gupta    private void postInflateProcess(View view, LayoutlibCallback layoutlibCallback, View skip)
6113bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            throws PostInflateException {
6128a16d5d203a48e7b13761f329333b3dcb8f8210bDeepanshu Gupta        if (view == skip) {
6138a16d5d203a48e7b13761f329333b3dcb8f8210bDeepanshu Gupta            return;
6148a16d5d203a48e7b13761f329333b3dcb8f8210bDeepanshu Gupta        }
6153bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (view instanceof TabHost) {
616b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Gupta            setupTabHost((TabHost) view, layoutlibCallback);
61762c9c3463a9b4a4bc899f5db353f2856876e6590Xavier Ducrohet        } else if (view instanceof QuickContactBadge) {
61862c9c3463a9b4a4bc899f5db353f2856876e6590Xavier Ducrohet            QuickContactBadge badge = (QuickContactBadge) view;
61962c9c3463a9b4a4bc899f5db353f2856876e6590Xavier Ducrohet            badge.setImageToDefault();
6207062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet        } else if (view instanceof AdapterView<?>) {
6217062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet            // get the view ID.
6227062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet            int id = view.getId();
6237062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet
6247062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet            BridgeContext context = getContext();
6257062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet
6267062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet            // get a ResourceReference from the integer ID.
6277062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet            ResourceReference listRef = context.resolveId(id);
6287062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet
6297062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet            if (listRef != null) {
6307062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                SessionParams params = getParams();
6317062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                AdapterBinding binding = params.getAdapterBindings().get(listRef);
6327062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet
6337062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                // if there was no adapter binding, trying to get it from the call back.
6347062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                if (binding == null) {
635b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Gupta                    binding = layoutlibCallback.getAdapterBinding(
636b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Gupta                            listRef, context.getViewKey(view), view);
6377062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                }
6387062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet
6397062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                if (binding != null) {
6407062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet
6417062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                    if (view instanceof AbsListView) {
6427062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                        if ((binding.getFooterCount() > 0 || binding.getHeaderCount() > 0) &&
6437062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                                view instanceof ListView) {
6447062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                            ListView list = (ListView) view;
6457062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet
6467062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                            boolean skipCallbackParser = false;
6477062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet
6487062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                            int count = binding.getHeaderCount();
6498a16d5d203a48e7b13761f329333b3dcb8f8210bDeepanshu Gupta                            for (int i = 0; i < count; i++) {
6507062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                                Pair<View, Boolean> pair = context.inflateView(
6517062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                                        binding.getHeaderAt(i),
652b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Gupta                                        list, false, skipCallbackParser);
6537062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                                if (pair.getFirst() != null) {
6547062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                                    list.addHeaderView(pair.getFirst());
6557062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                                }
6567062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet
6577062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                                skipCallbackParser |= pair.getSecond();
6587062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                            }
6597062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet
6607062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                            count = binding.getFooterCount();
6618a16d5d203a48e7b13761f329333b3dcb8f8210bDeepanshu Gupta                            for (int i = 0; i < count; i++) {
6627062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                                Pair<View, Boolean> pair = context.inflateView(
6637062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                                        binding.getFooterAt(i),
664b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Gupta                                        list, false, skipCallbackParser);
6657062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                                if (pair.getFirst() != null) {
6667062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                                    list.addFooterView(pair.getFirst());
6677062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                                }
6687062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet
6697062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                                skipCallbackParser |= pair.getSecond();
6707062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                            }
6717062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                        }
6727062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet
6737062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                        if (view instanceof ExpandableListView) {
6747062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                            ((ExpandableListView) view).setAdapter(
675b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Gupta                                    new FakeExpandableAdapter(listRef, binding, layoutlibCallback));
6767062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                        } else {
6777062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                            ((AbsListView) view).setAdapter(
678b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Gupta                                    new FakeAdapter(listRef, binding, layoutlibCallback));
6797062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                        }
6807062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                    } else if (view instanceof AbsSpinner) {
6817062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                        ((AbsSpinner) view).setAdapter(
682b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Gupta                                new FakeAdapter(listRef, binding, layoutlibCallback));
6837062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                    }
6847062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet                }
6857062c1ad9adcb216ac49100ca1044e9d89e8f8c1Xavier Ducrohet            }
6863bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        } else if (view instanceof ViewGroup) {
6879028fa93da0f9c7dad2176de347cd6e705084c9fDeepanshu Gupta            mInflater.postInflateProcess(view);
6888a16d5d203a48e7b13761f329333b3dcb8f8210bDeepanshu Gupta            ViewGroup group = (ViewGroup) view;
6893bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            final int count = group.getChildCount();
6908a16d5d203a48e7b13761f329333b3dcb8f8210bDeepanshu Gupta            for (int c = 0; c < count; c++) {
6913bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                View child = group.getChildAt(c);
692b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Gupta                postInflateProcess(child, layoutlibCallback, skip);
6933bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            }
6943bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
6953bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
6963bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
6973bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    /**
698c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen     * If the root layout is a CoordinatorLayout with an AppBar:
699c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen     * Set the title of the AppBar to the title of the activity context.
700c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen     */
701c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen    private void setActiveToolbar(View view, BridgeContext context, SessionParams params) {
702c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        View coordinatorLayout = findChildView(view, DesignLibUtil.CN_COORDINATOR_LAYOUT);
703c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        if (coordinatorLayout == null) {
704c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen            return;
705c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        }
706c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        View appBar = findChildView(coordinatorLayout, DesignLibUtil.CN_APPBAR_LAYOUT);
707c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        if (appBar == null) {
708c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen            return;
709c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        }
710c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        ViewGroup collapsingToolbar =
711c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen                (ViewGroup) findChildView(appBar, DesignLibUtil.CN_COLLAPSING_TOOLBAR_LAYOUT);
712c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        if (collapsingToolbar == null) {
713c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen            return;
714c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        }
715c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        if (!hasToolbar(collapsingToolbar)) {
716c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen            return;
717c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        }
718c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        RenderResources res = context.getRenderResources();
719c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        String title = params.getAppLabel();
720c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        ResourceValue titleValue = res.findResValue(title, false);
721c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        if (titleValue != null && titleValue.getValue() != null) {
722c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen            title = titleValue.getValue();
723c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        }
724c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        DesignLibUtil.setTitle(collapsingToolbar, title);
725c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen    }
726c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen
727c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen    private View findChildView(View view, String className) {
728c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        if (!(view instanceof ViewGroup)) {
729c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen            return null;
730c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        }
731c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        ViewGroup group = (ViewGroup) view;
732c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        for (int i = 0; i < group.getChildCount(); i++) {
733c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen            if (isInstanceOf(group.getChildAt(i), className)) {
734c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen                return group.getChildAt(i);
735c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen            }
736c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        }
737c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        return null;
738c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen    }
739c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen
740c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen    private boolean hasToolbar(View collapsingToolbar) {
741c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        if (!(collapsingToolbar instanceof ViewGroup)) {
742c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen            return false;
743c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        }
744c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        ViewGroup group = (ViewGroup) collapsingToolbar;
745c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        for (int i = 0; i < group.getChildCount(); i++) {
746c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen            if (isInstanceOf(group.getChildAt(i), DesignLibUtil.CN_TOOLBAR)) {
747c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen                return true;
748c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen            }
749c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        }
750c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        return false;
751c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen    }
752c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen
753c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen    /**
754777163c3facaed668b43061d726a572fd58f6f60Diego Perez     * Set the scroll position on all the components with the "scrollX" and "scrollY" attribute. If
755777163c3facaed668b43061d726a572fd58f6f60Diego Perez     * the component supports nested scrolling attempt that first, then use the unconsumed scroll
756777163c3facaed668b43061d726a572fd58f6f60Diego Perez     * part to scroll the content in the component.
757c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen     */
7580685ab35ad78094ceb2d9fb0583e2cadd1968c90Diego Perez    private static void handleScrolling(BridgeContext context, View view) {
759777163c3facaed668b43061d726a572fd58f6f60Diego Perez        int scrollPosX = context.getScrollXPos(view);
760777163c3facaed668b43061d726a572fd58f6f60Diego Perez        int scrollPosY = context.getScrollYPos(view);
761777163c3facaed668b43061d726a572fd58f6f60Diego Perez        if (scrollPosX != 0 || scrollPosY != 0) {
762c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen            if (view.isNestedScrollingEnabled()) {
763c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen                int[] consumed = new int[2];
764777163c3facaed668b43061d726a572fd58f6f60Diego Perez                int axis = scrollPosX != 0 ? View.SCROLL_AXIS_HORIZONTAL : 0;
765777163c3facaed668b43061d726a572fd58f6f60Diego Perez                axis |= scrollPosY != 0 ? View.SCROLL_AXIS_VERTICAL : 0;
766777163c3facaed668b43061d726a572fd58f6f60Diego Perez                if (view.startNestedScroll(axis)) {
767777163c3facaed668b43061d726a572fd58f6f60Diego Perez                    view.dispatchNestedPreScroll(scrollPosX, scrollPosY, consumed, null);
768777163c3facaed668b43061d726a572fd58f6f60Diego Perez                    view.dispatchNestedScroll(consumed[0], consumed[1], scrollPosX, scrollPosY,
769777163c3facaed668b43061d726a572fd58f6f60Diego Perez                            null);
770c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen                    view.stopNestedScroll();
771777163c3facaed668b43061d726a572fd58f6f60Diego Perez                    scrollPosX -= consumed[0];
772777163c3facaed668b43061d726a572fd58f6f60Diego Perez                    scrollPosY -= consumed[1];
773c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen                }
774c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen            }
775777163c3facaed668b43061d726a572fd58f6f60Diego Perez            if (scrollPosX != 0 || scrollPosY != 0) {
7760685ab35ad78094ceb2d9fb0583e2cadd1968c90Diego Perez                view.scrollTo(scrollPosX, scrollPosY);
777c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen            }
778c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        }
779c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen
780c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        if (!(view instanceof ViewGroup)) {
781c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen            return;
782c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        }
783c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        ViewGroup group = (ViewGroup) view;
784c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        for (int i = 0; i < group.getChildCount(); i++) {
785c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen            View child = group.getChildAt(i);
7860685ab35ad78094ceb2d9fb0583e2cadd1968c90Diego Perez            handleScrolling(context, child);
787c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen        }
788c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen    }
789c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen
790c46c84ee673934f340b27b40366a5a61669a4a1eJens Ole Lauridsen    /**
7913bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * Sets up a {@link TabHost} object.
7923bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     * @param tabHost the TabHost to setup.
793b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Gupta     * @param layoutlibCallback The project callback object to access the project R class.
794bca9e5623c820c9aa1e959e3874442de9a9a6c32Diego Perez     * @throws PostInflateException if TabHost is missing the required ids for TabHost
7953bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     */
796b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Gupta    private void setupTabHost(TabHost tabHost, LayoutlibCallback layoutlibCallback)
7973bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            throws PostInflateException {
7983bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // look for the TabWidget, and the FrameLayout. They have their own specific names
7993bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        View v = tabHost.findViewById(android.R.id.tabs);
8003bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
8013bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (v == null) {
8023bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            throw new PostInflateException(
8033bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    "TabHost requires a TabWidget with id \"android:id/tabs\".\n");
8043bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
8053bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
806952c989b307d4b710ee1adba064f663808af9eb3Deepanshu Gupta        if (!(v instanceof TabWidget)) {
8073bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            throw new PostInflateException(String.format(
8083bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    "TabHost requires a TabWidget with id \"android:id/tabs\".\n" +
8093bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    "View found with id 'tabs' is '%s'", v.getClass().getCanonicalName()));
8103bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
8113bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
8123bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        v = tabHost.findViewById(android.R.id.tabcontent);
8133bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
8143bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (v == null) {
815952c989b307d4b710ee1adba064f663808af9eb3Deepanshu Gupta            // TODO: see if we can fake tabs even without the FrameLayout (same below when the frameLayout is empty)
816952c989b307d4b710ee1adba064f663808af9eb3Deepanshu Gupta            //noinspection SpellCheckingInspection
8173bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            throw new PostInflateException(
8183bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    "TabHost requires a FrameLayout with id \"android:id/tabcontent\".");
8193bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
8203bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
821952c989b307d4b710ee1adba064f663808af9eb3Deepanshu Gupta        if (!(v instanceof FrameLayout)) {
822952c989b307d4b710ee1adba064f663808af9eb3Deepanshu Gupta            //noinspection SpellCheckingInspection
8233bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            throw new PostInflateException(String.format(
8243bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    "TabHost requires a FrameLayout with id \"android:id/tabcontent\".\n" +
8253bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet                    "View found with id 'tabcontent' is '%s'", v.getClass().getCanonicalName()));
8263bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
8273bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
8283bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        FrameLayout content = (FrameLayout)v;
8293bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
830952c989b307d4b710ee1adba064f663808af9eb3Deepanshu Gupta        // now process the content of the frameLayout and dynamically create tabs for it.
8313bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        final int count = content.getChildCount();
8323bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
8333bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // this must be called before addTab() so that the TabHost searches its TabWidget
8343bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        // and FrameLayout.
835f59abd4701479e496d955cb8521a813e535aefa3Jerome Gaillard        if (ReflectionUtils.isInstanceOf(tabHost, FragmentTabHostUtil.CN_FRAGMENT_TAB_HOST)) {
836f59abd4701479e496d955cb8521a813e535aefa3Jerome Gaillard            FragmentTabHostUtil.setup(tabHost, getContext());
837f59abd4701479e496d955cb8521a813e535aefa3Jerome Gaillard        } else {
838f59abd4701479e496d955cb8521a813e535aefa3Jerome Gaillard            tabHost.setup();
839f59abd4701479e496d955cb8521a813e535aefa3Jerome Gaillard        }
8403bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
841f14c342b8a0024e5ca8c2c3ffce8b79abe03e941Tor Norbye        if (count == 0) {
842f14c342b8a0024e5ca8c2c3ffce8b79abe03e941Tor Norbye            // Create a dummy child to get a single tab
8436257175afa5c892cb0a7cd14d958418c1720cb84Deepanshu Gupta            TabSpec spec = tabHost.newTabSpec("tag")
8446257175afa5c892cb0a7cd14d958418c1720cb84Deepanshu Gupta                    .setIndicator("Tab Label", tabHost.getResources()
8456257175afa5c892cb0a7cd14d958418c1720cb84Deepanshu Gupta                            .getDrawable(android.R.drawable.ic_menu_info_details, null))
846bca9e5623c820c9aa1e959e3874442de9a9a6c32Diego Perez                    .setContent(tag -> new LinearLayout(getContext()));
847f14c342b8a0024e5ca8c2c3ffce8b79abe03e941Tor Norbye            tabHost.addTab(spec);
848f14c342b8a0024e5ca8c2c3ffce8b79abe03e941Tor Norbye        } else {
849952c989b307d4b710ee1adba064f663808af9eb3Deepanshu Gupta            // for each child of the frameLayout, add a new TabSpec
850f14c342b8a0024e5ca8c2c3ffce8b79abe03e941Tor Norbye            for (int i = 0 ; i < count ; i++) {
851f14c342b8a0024e5ca8c2c3ffce8b79abe03e941Tor Norbye                View child = content.getChildAt(i);
852f14c342b8a0024e5ca8c2c3ffce8b79abe03e941Tor Norbye                String tabSpec = String.format("tab_spec%d", i+1);
8538a16d5d203a48e7b13761f329333b3dcb8f8210bDeepanshu Gupta                @SuppressWarnings("ConstantConditions")  // child cannot be null.
854f14c342b8a0024e5ca8c2c3ffce8b79abe03e941Tor Norbye                int id = child.getId();
855952c989b307d4b710ee1adba064f663808af9eb3Deepanshu Gupta                @SuppressWarnings("deprecation")
856b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Gupta                Pair<ResourceType, String> resource = layoutlibCallback.resolveResourceId(id);
857f14c342b8a0024e5ca8c2c3ffce8b79abe03e941Tor Norbye                String name;
858f14c342b8a0024e5ca8c2c3ffce8b79abe03e941Tor Norbye                if (resource != null) {
859947b979a91ac1cd556244d875d8b8d4fec8fd474Xavier Ducrohet                    name = resource.getSecond();
860f14c342b8a0024e5ca8c2c3ffce8b79abe03e941Tor Norbye                } else {
861f14c342b8a0024e5ca8c2c3ffce8b79abe03e941Tor Norbye                    name = String.format("Tab %d", i+1); // default name if id is unresolved.
862f14c342b8a0024e5ca8c2c3ffce8b79abe03e941Tor Norbye                }
863f14c342b8a0024e5ca8c2c3ffce8b79abe03e941Tor Norbye                tabHost.addTab(tabHost.newTabSpec(tabSpec).setIndicator(name).setContent(id));
8643bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            }
8653bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
8663bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
8673bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
86885c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta    /**
86985c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta     * Visits a {@link View} and its children and generate a {@link ViewInfo} containing the
87085c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta     * bounds of all the views.
87185c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta     *
87285c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta     * @param view the root View
873e48b02f441a8a90f0a1618ed2116c83441aae3bdDiego Perez     * @param hOffset horizontal offset for the view bounds.
874e48b02f441a8a90f0a1618ed2116c83441aae3bdDiego Perez     * @param vOffset vertical offset for the view bounds.
87585c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta     * @param setExtendedInfo whether to set the extended view info in the {@link ViewInfo} object.
87685c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta     * @param isContentFrame {@code true} if the {@code ViewInfo} to be created is part of the
87785c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta     *                       content frame.
87885c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta     *
87985c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta     * @return {@code ViewInfo} containing the bounds of the view and it children otherwise.
88085c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta     */
881e48b02f441a8a90f0a1618ed2116c83441aae3bdDiego Perez    private ViewInfo visit(View view, int hOffset, int vOffset, boolean setExtendedInfo,
88285c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta            boolean isContentFrame) {
883e48b02f441a8a90f0a1618ed2116c83441aae3bdDiego Perez        ViewInfo result = createViewInfo(view, hOffset, vOffset, setExtendedInfo, isContentFrame);
884799fe3b371c812a0e94872f896b54afa44e94868Xavier Ducrohet
88585c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta        if (view instanceof ViewGroup) {
88685c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta            ViewGroup group = ((ViewGroup) view);
887e48b02f441a8a90f0a1618ed2116c83441aae3bdDiego Perez            result.setChildren(visitAllChildren(group, isContentFrame ? 0 : hOffset,
888e48b02f441a8a90f0a1618ed2116c83441aae3bdDiego Perez                    isContentFrame ? 0 : vOffset,
88985c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta                    setExtendedInfo, isContentFrame));
89085c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta        }
89185c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta        return result;
89285c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta    }
893799fe3b371c812a0e94872f896b54afa44e94868Xavier Ducrohet
89485c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta    /**
89585c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta     * Visits all the children of a given ViewGroup and generates a list of {@link ViewInfo}
8968d43509cc2c0f95bd07d6585dadfac296bc5d24dDeepanshu Gupta     * containing the bounds of all the views. It also initializes the {@link #mViewInfoList} with
89785c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta     * the children of the {@code mContentRoot}.
89885c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta     *
89985c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta     * @param viewGroup the root View
900e48b02f441a8a90f0a1618ed2116c83441aae3bdDiego Perez     * @param hOffset horizontal offset from the top for the content view frame.
901e48b02f441a8a90f0a1618ed2116c83441aae3bdDiego Perez     * @param vOffset vertical offset from the top for the content view frame.
90285c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta     * @param setExtendedInfo whether to set the extended view info in the {@link ViewInfo} object.
90385c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta     * @param isContentFrame {@code true} if the {@code ViewInfo} to be created is part of the
90485c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta     *                       content frame. {@code false} if the {@code ViewInfo} to be created is
90585c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta     *                       part of the system decor.
90685c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta     */
907e48b02f441a8a90f0a1618ed2116c83441aae3bdDiego Perez    private List<ViewInfo> visitAllChildren(ViewGroup viewGroup, int hOffset, int vOffset,
90885c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta            boolean setExtendedInfo, boolean isContentFrame) {
90985c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta        if (viewGroup == null) {
91085c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta            return null;
911799fe3b371c812a0e94872f896b54afa44e94868Xavier Ducrohet        }
912799fe3b371c812a0e94872f896b54afa44e94868Xavier Ducrohet
91385c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta        if (!isContentFrame) {
914e48b02f441a8a90f0a1618ed2116c83441aae3bdDiego Perez            vOffset += viewGroup.getTop();
915e48b02f441a8a90f0a1618ed2116c83441aae3bdDiego Perez            hOffset += viewGroup.getLeft();
91685c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta        }
917799fe3b371c812a0e94872f896b54afa44e94868Xavier Ducrohet
91885c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta        int childCount = viewGroup.getChildCount();
91985c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta        if (viewGroup == mContentRoot) {
920bca9e5623c820c9aa1e959e3874442de9a9a6c32Diego Perez            List<ViewInfo> childrenWithoutOffset = new ArrayList<>(childCount);
921bca9e5623c820c9aa1e959e3874442de9a9a6c32Diego Perez            List<ViewInfo> childrenWithOffset = new ArrayList<>(childCount);
92285c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta            for (int i = 0; i < childCount; i++) {
923e48b02f441a8a90f0a1618ed2116c83441aae3bdDiego Perez                ViewInfo[] childViewInfo =
924e48b02f441a8a90f0a1618ed2116c83441aae3bdDiego Perez                        visitContentRoot(viewGroup.getChildAt(i), hOffset, vOffset,
9256208081afd488af51d4f7cbf858e2de59d47e4e5Xavier Ducrohet                        setExtendedInfo);
92685c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta                childrenWithoutOffset.add(childViewInfo[0]);
92785c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta                childrenWithOffset.add(childViewInfo[1]);
928799fe3b371c812a0e94872f896b54afa44e94868Xavier Ducrohet            }
92985c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta            mViewInfoList = childrenWithOffset;
93085c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta            return childrenWithoutOffset;
93185c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta        } else {
932bca9e5623c820c9aa1e959e3874442de9a9a6c32Diego Perez            List<ViewInfo> children = new ArrayList<>(childCount);
93385c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta            for (int i = 0; i < childCount; i++) {
934e48b02f441a8a90f0a1618ed2116c83441aae3bdDiego Perez                children.add(visit(viewGroup.getChildAt(i), hOffset, vOffset, setExtendedInfo,
93585c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta                        isContentFrame));
93685c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta            }
93785c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta            return children;
938799fe3b371c812a0e94872f896b54afa44e94868Xavier Ducrohet        }
93985c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta    }
940799fe3b371c812a0e94872f896b54afa44e94868Xavier Ducrohet
94185c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta    /**
94285c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta     * Visits the children of {@link #mContentRoot} and generates {@link ViewInfo} containing the
94385c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta     * bounds of all the views. It returns two {@code ViewInfo} objects with the same children,
94485c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta     * one with the {@code offset} and other without the {@code offset}. The offset is needed to
94585c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta     * get the right bounds if the {@code ViewInfo} hierarchy is accessed from
94685c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta     * {@code mViewInfoList}. When the hierarchy is accessed via {@code mSystemViewInfoList}, the
94785c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta     * offset is not needed.
94885c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta     *
94985c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta     * @return an array of length two, with ViewInfo at index 0 is without offset and ViewInfo at
95085c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta     *         index 1 is with the offset.
95185c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta     */
952432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta    @NonNull
953e48b02f441a8a90f0a1618ed2116c83441aae3bdDiego Perez    private ViewInfo[] visitContentRoot(View view, int hOffset, int vOffset,
954e48b02f441a8a90f0a1618ed2116c83441aae3bdDiego Perez            boolean setExtendedInfo) {
95585c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta        ViewInfo[] result = new ViewInfo[2];
95685c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta        if (view == null) {
95785c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta            return result;
95885c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta        }
95985c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta
960e48b02f441a8a90f0a1618ed2116c83441aae3bdDiego Perez        result[0] = createViewInfo(view, 0, 0, setExtendedInfo, true);
961e48b02f441a8a90f0a1618ed2116c83441aae3bdDiego Perez        result[1] = createViewInfo(view, hOffset, vOffset, setExtendedInfo, true);
96285c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta        if (view instanceof ViewGroup) {
963e48b02f441a8a90f0a1618ed2116c83441aae3bdDiego Perez            List<ViewInfo> children =
964e48b02f441a8a90f0a1618ed2116c83441aae3bdDiego Perez                    visitAllChildren((ViewGroup) view, 0, 0, setExtendedInfo, true);
96585c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta            result[0].setChildren(children);
96685c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta            result[1].setChildren(children);
96785c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta        }
96885c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta        return result;
969799fe3b371c812a0e94872f896b54afa44e94868Xavier Ducrohet    }
9703bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
9713bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    /**
97285c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta     * Creates a {@link ViewInfo} for the view. The {@code ViewInfo} corresponding to the children
97385c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta     * of the {@code view} are not created. Consequently, the children of {@code ViewInfo} is not
97485c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta     * set.
975e48b02f441a8a90f0a1618ed2116c83441aae3bdDiego Perez     * @param hOffset horizontal offset for the view bounds. Used only if view is part of the
976e48b02f441a8a90f0a1618ed2116c83441aae3bdDiego Perez     * content frame.
977e48b02f441a8a90f0a1618ed2116c83441aae3bdDiego Perez     * @param vOffset vertial an offset for the view bounds. Used only if view is part of the
978e48b02f441a8a90f0a1618ed2116c83441aae3bdDiego Perez     * content frame.
9793bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet     */
980e48b02f441a8a90f0a1618ed2116c83441aae3bdDiego Perez    private ViewInfo createViewInfo(View view, int hOffset, int vOffset, boolean setExtendedInfo,
98185c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta            boolean isContentFrame) {
9823bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        if (view == null) {
9833bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet            return null;
9843bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        }
9853bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
986777163c3facaed668b43061d726a572fd58f6f60Diego Perez        ViewParent parent = view.getParent();
98785c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta        ViewInfo result;
98885c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta        if (isContentFrame) {
989777163c3facaed668b43061d726a572fd58f6f60Diego Perez            // Account for parent scroll values when calculating the bounding box
990777163c3facaed668b43061d726a572fd58f6f60Diego Perez            int scrollX = parent != null ? ((View)parent).getScrollX() : 0;
991777163c3facaed668b43061d726a572fd58f6f60Diego Perez            int scrollY = parent != null ? ((View)parent).getScrollY() : 0;
992777163c3facaed668b43061d726a572fd58f6f60Diego Perez
99370114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta            // The view is part of the layout added by the user. Hence,
99470114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta            // the ViewCookie may be obtained only through the Context.
99585c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta            result = new ViewInfo(view.getClass().getName(),
996e48b02f441a8a90f0a1618ed2116c83441aae3bdDiego Perez                    getContext().getViewKey(view), -scrollX + view.getLeft() + hOffset,
997e48b02f441a8a90f0a1618ed2116c83441aae3bdDiego Perez                    -scrollY + view.getTop() + vOffset, -scrollX + view.getRight() + hOffset,
998e48b02f441a8a90f0a1618ed2116c83441aae3bdDiego Perez                    -scrollY + view.getBottom() + vOffset,
999777163c3facaed668b43061d726a572fd58f6f60Diego Perez                    view, view.getLayoutParams());
100085c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta        } else {
100170114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta            // We are part of the system decor.
100270114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta            SystemViewInfo r = new SystemViewInfo(view.getClass().getName(),
100341670df819e35f5822cc166db775ed2e9e3dfb7dDeepanshu Gupta                    getViewKey(view),
100485c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta                    view.getLeft(), view.getTop(), view.getRight(),
100585c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta                    view.getBottom(), view, view.getLayoutParams());
100670114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta            result = r;
100770114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta            // We currently mark three kinds of views:
100870114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta            // 1. Menus in the Action Bar
100970114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta            // 2. Menus in the Overflow popup.
101070114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta            // 3. The overflow popup button.
101170114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta            if (view instanceof ListMenuItemView) {
101270114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta                // Mark 2.
101370114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta                // All menus in the popup are of type ListMenuItemView.
101470114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta                r.setViewType(ViewType.ACTION_BAR_OVERFLOW_MENU);
101570114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta            } else {
101670114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta                // Mark 3.
101770114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta                ViewGroup.LayoutParams lp = view.getLayoutParams();
101870114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta                if (lp instanceof ActionMenuView.LayoutParams &&
101970114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta                        ((ActionMenuView.LayoutParams) lp).isOverflowButton) {
102070114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta                    r.setViewType(ViewType.ACTION_BAR_OVERFLOW);
102170114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta                } else {
102270114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta                    // Mark 1.
102370114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta                    // A view is a menu in the Action Bar is it is not the overflow button and of
102470114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta                    // its parent is of type ActionMenuView. We can also check if the view is
102570114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta                    // instanceof ActionMenuItemView but that will fail for menus using
102670114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta                    // actionProviderClass.
102770114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta                    while (parent != mViewRoot && parent instanceof ViewGroup) {
102870114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta                        if (parent instanceof ActionMenuView) {
102970114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta                            r.setViewType(ViewType.ACTION_BAR_MENU);
103070114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta                            break;
103170114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta                        }
103270114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta                        parent = parent.getParent();
103370114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta                    }
103470114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta                }
103570114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta            }
103685c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta        }
10373bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
10386208081afd488af51d4f7cbf858e2de59d47e4e5Xavier Ducrohet        if (setExtendedInfo) {
10396208081afd488af51d4f7cbf858e2de59d47e4e5Xavier Ducrohet            MarginLayoutParams marginParams = null;
10406208081afd488af51d4f7cbf858e2de59d47e4e5Xavier Ducrohet            LayoutParams params = view.getLayoutParams();
10416208081afd488af51d4f7cbf858e2de59d47e4e5Xavier Ducrohet            if (params instanceof MarginLayoutParams) {
10426208081afd488af51d4f7cbf858e2de59d47e4e5Xavier Ducrohet                marginParams = (MarginLayoutParams) params;
10436208081afd488af51d4f7cbf858e2de59d47e4e5Xavier Ducrohet            }
10446208081afd488af51d4f7cbf858e2de59d47e4e5Xavier Ducrohet            result.setExtendedInfo(view.getBaseline(),
10456208081afd488af51d4f7cbf858e2de59d47e4e5Xavier Ducrohet                    marginParams != null ? marginParams.leftMargin : 0,
10466208081afd488af51d4f7cbf858e2de59d47e4e5Xavier Ducrohet                    marginParams != null ? marginParams.topMargin : 0,
10476208081afd488af51d4f7cbf858e2de59d47e4e5Xavier Ducrohet                    marginParams != null ? marginParams.rightMargin : 0,
10486208081afd488af51d4f7cbf858e2de59d47e4e5Xavier Ducrohet                    marginParams != null ? marginParams.bottomMargin : 0);
10496208081afd488af51d4f7cbf858e2de59d47e4e5Xavier Ducrohet        }
10506208081afd488af51d4f7cbf858e2de59d47e4e5Xavier Ducrohet
10513bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        return result;
10523bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
10533bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
105470114b33f8054cc38090a2bbd213ebf15abee63dDeepanshu Gupta    /* (non-Javadoc)
105541670df819e35f5822cc166db775ed2e9e3dfb7dDeepanshu Gupta     * The cookie for menu items are stored in menu item and not in the map from View stored in
105641670df819e35f5822cc166db775ed2e9e3dfb7dDeepanshu Gupta     * BridgeContext.
105741670df819e35f5822cc166db775ed2e9e3dfb7dDeepanshu Gupta     */
1058432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta    @Nullable
105941670df819e35f5822cc166db775ed2e9e3dfb7dDeepanshu Gupta    private Object getViewKey(View view) {
106041670df819e35f5822cc166db775ed2e9e3dfb7dDeepanshu Gupta        BridgeContext context = getContext();
106141670df819e35f5822cc166db775ed2e9e3dfb7dDeepanshu Gupta        if (!(view instanceof MenuView.ItemView)) {
106241670df819e35f5822cc166db775ed2e9e3dfb7dDeepanshu Gupta            return context.getViewKey(view);
106341670df819e35f5822cc166db775ed2e9e3dfb7dDeepanshu Gupta        }
106441670df819e35f5822cc166db775ed2e9e3dfb7dDeepanshu Gupta        MenuItemImpl menuItem;
106541670df819e35f5822cc166db775ed2e9e3dfb7dDeepanshu Gupta        if (view instanceof ActionMenuItemView) {
106641670df819e35f5822cc166db775ed2e9e3dfb7dDeepanshu Gupta            menuItem = ((ActionMenuItemView) view).getItemData();
106741670df819e35f5822cc166db775ed2e9e3dfb7dDeepanshu Gupta        } else if (view instanceof ListMenuItemView) {
106841670df819e35f5822cc166db775ed2e9e3dfb7dDeepanshu Gupta            menuItem = ((ListMenuItemView) view).getItemData();
106941670df819e35f5822cc166db775ed2e9e3dfb7dDeepanshu Gupta        } else if (view instanceof IconMenuItemView) {
107041670df819e35f5822cc166db775ed2e9e3dfb7dDeepanshu Gupta            menuItem = ((IconMenuItemView) view).getItemData();
107141670df819e35f5822cc166db775ed2e9e3dfb7dDeepanshu Gupta        } else {
107241670df819e35f5822cc166db775ed2e9e3dfb7dDeepanshu Gupta            menuItem = null;
107341670df819e35f5822cc166db775ed2e9e3dfb7dDeepanshu Gupta        }
107441670df819e35f5822cc166db775ed2e9e3dfb7dDeepanshu Gupta        if (menuItem instanceof BridgeMenuItemImpl) {
107541670df819e35f5822cc166db775ed2e9e3dfb7dDeepanshu Gupta            return ((BridgeMenuItemImpl) menuItem).getViewCookie();
107641670df819e35f5822cc166db775ed2e9e3dfb7dDeepanshu Gupta        }
107741670df819e35f5822cc166db775ed2e9e3dfb7dDeepanshu Gupta
107841670df819e35f5822cc166db775ed2e9e3dfb7dDeepanshu Gupta        return null;
107941670df819e35f5822cc166db775ed2e9e3dfb7dDeepanshu Gupta    }
108041670df819e35f5822cc166db775ed2e9e3dfb7dDeepanshu Gupta
1081620f80a7ee9796c2c41ec0a42d10d2913fc50c90Diego Perez    public void invalidateRenderingSize() {
108285c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta        mMeasuredScreenWidth = mMeasuredScreenHeight = -1;
108385c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta    }
108485c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta
10853bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    public BufferedImage getImage() {
10863bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet        return mImage;
10873bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
10883bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet
1089a6a38c02bfd3f6a678ac1671f5744b8e439e075aXavier Ducrohet    public boolean isAlphaChannelImage() {
1090a6a38c02bfd3f6a678ac1671f5744b8e439e075aXavier Ducrohet        return mIsAlphaChannelImage;
1091a6a38c02bfd3f6a678ac1671f5744b8e439e075aXavier Ducrohet    }
1092a6a38c02bfd3f6a678ac1671f5744b8e439e075aXavier Ducrohet
1093e9a2ea4ac32589b372ec195d8be59391c686a8c6Xavier Ducrohet    public List<ViewInfo> getViewInfos() {
1094e9a2ea4ac32589b372ec195d8be59391c686a8c6Xavier Ducrohet        return mViewInfoList;
10953bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet    }
10962d240967c9bc38cbf69967457b33f953f8826e96Xavier Ducrohet
109785c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta    public List<ViewInfo> getSystemViewInfos() {
109885c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta        return mSystemViewInfoList;
109985c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta    }
110085c5ae75ab4b9041bb143f146ef5fc4547686210Deepanshu Gupta
110118be29a3d14f1b3e840e8ee88b5a1951a6f6e9c8Deepanshu Gupta    public Map<Object, PropertiesMap> getDefaultProperties() {
110218be29a3d14f1b3e840e8ee88b5a1951a6f6e9c8Deepanshu Gupta        return getContext().getDefaultProperties();
110318be29a3d14f1b3e840e8ee88b5a1951a6f6e9c8Deepanshu Gupta    }
110418be29a3d14f1b3e840e8ee88b5a1951a6f6e9c8Deepanshu Gupta
1105ecb67b629266bd616da571c1e391164a4f8c8e93Xavier Ducrohet    public void setScene(RenderSession session) {
1106ecb67b629266bd616da571c1e391164a4f8c8e93Xavier Ducrohet        mScene = session;
1107b12b63f973ebf8fdc4f36f77dd29bd1bed436609Xavier Ducrohet    }
1108b12b63f973ebf8fdc4f36f77dd29bd1bed436609Xavier Ducrohet
1109ecb67b629266bd616da571c1e391164a4f8c8e93Xavier Ducrohet    public RenderSession getSession() {
1110b12b63f973ebf8fdc4f36f77dd29bd1bed436609Xavier Ducrohet        return mScene;
1111b12b63f973ebf8fdc4f36f77dd29bd1bed436609Xavier Ducrohet    }
111295eceea4f12c4f0e5170b1a14658f23e87e343d5Diego Perez
111395eceea4f12c4f0e5170b1a14658f23e87e343d5Diego Perez    public void dispose() {
1114e4cf18face6980b0c7ac42761512cd93b89927c3Diego Perez        boolean createdLooper = false;
1115e4cf18face6980b0c7ac42761512cd93b89927c3Diego Perez        if (Looper.myLooper() == null) {
1116e4cf18face6980b0c7ac42761512cd93b89927c3Diego Perez            // Detaching the root view from the window will try to stop any running animations.
1117e4cf18face6980b0c7ac42761512cd93b89927c3Diego Perez            // The stop method checks that it can run in the looper so, if there is no current
1118e4cf18face6980b0c7ac42761512cd93b89927c3Diego Perez            // looper, we create a temporary one to complete the shutdown.
1119e4cf18face6980b0c7ac42761512cd93b89927c3Diego Perez            Bridge.prepareThread();
1120e4cf18face6980b0c7ac42761512cd93b89927c3Diego Perez            createdLooper = true;
1121e4cf18face6980b0c7ac42761512cd93b89927c3Diego Perez        }
112295eceea4f12c4f0e5170b1a14658f23e87e343d5Diego Perez        AttachInfo_Accessor.detachFromWindow(mViewRoot);
112395eceea4f12c4f0e5170b1a14658f23e87e343d5Diego Perez        if (mCanvas != null) {
112495eceea4f12c4f0e5170b1a14658f23e87e343d5Diego Perez            mCanvas.release();
112595eceea4f12c4f0e5170b1a14658f23e87e343d5Diego Perez            mCanvas = null;
112695eceea4f12c4f0e5170b1a14658f23e87e343d5Diego Perez        }
112795eceea4f12c4f0e5170b1a14658f23e87e343d5Diego Perez        if (mViewInfoList != null) {
112895eceea4f12c4f0e5170b1a14658f23e87e343d5Diego Perez            mViewInfoList.clear();
112995eceea4f12c4f0e5170b1a14658f23e87e343d5Diego Perez        }
113095eceea4f12c4f0e5170b1a14658f23e87e343d5Diego Perez        if (mSystemViewInfoList != null) {
113195eceea4f12c4f0e5170b1a14658f23e87e343d5Diego Perez            mSystemViewInfoList.clear();
113295eceea4f12c4f0e5170b1a14658f23e87e343d5Diego Perez        }
113395eceea4f12c4f0e5170b1a14658f23e87e343d5Diego Perez        mImage = null;
113495eceea4f12c4f0e5170b1a14658f23e87e343d5Diego Perez        mViewRoot = null;
113595eceea4f12c4f0e5170b1a14658f23e87e343d5Diego Perez        mContentRoot = null;
1136e4cf18face6980b0c7ac42761512cd93b89927c3Diego Perez
1137e4cf18face6980b0c7ac42761512cd93b89927c3Diego Perez        if (createdLooper) {
1138405d10472d7b8ba7ff86f46e734195128709a829Deepanshu Gupta            Choreographer_Delegate.dispose();
113967f948f353b652d3373c7bdb0c2a8715e8b4e9f6Diego Perez            Bridge.cleanupThread();
1140e4cf18face6980b0c7ac42761512cd93b89927c3Diego Perez        }
114195eceea4f12c4f0e5170b1a14658f23e87e343d5Diego Perez    }
11423bd98986a97e4e1921616a0a86983307e68ceb6cXavier Ducrohet}
1143