Bug 909550 - Lazy-inflate TabsPanel once the first pageload is finished (r=mfinkle, a=sylvestre)
authorLucas Rocha <lucasr@mozilla.com>
Thu, 06 Feb 2014 08:59:50 +0000
changeset 176899 01b6fb4e6ad905d46e46cf0863cc3772f688c6b4
parent 176898 91a57ce52263b7007fe394cdbe5acd72324a8a78
child 176900 29b872b75a919342243eb92f9ce3ab51653324a1
push id5195
push userlrocha@mozilla.com
push dateFri, 07 Feb 2014 11:12:09 +0000
treeherdermozilla-aurora@01b6fb4e6ad9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmfinkle, sylvestre
bugs909550
milestone29.0a2
Bug 909550 - Lazy-inflate TabsPanel once the first pageload is finished (r=mfinkle, a=sylvestre)
mobile/android/base/BrowserApp.java
mobile/android/base/GeckoApp.java
mobile/android/base/resources/layout/gecko_app.xml
mobile/android/base/resources/layout/tabs_panel_view.xml
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -68,16 +68,17 @@ import android.view.KeyEvent;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.MotionEvent;
 import android.view.SubMenu;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewStub;
+import android.view.ViewTreeObserver;
 import android.view.animation.Interpolator;
 import android.widget.LinearLayout;
 import android.widget.RelativeLayout;
 import android.widget.Toast;
 import android.widget.ViewFlipper;
 
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -114,16 +115,17 @@ abstract public class BrowserApp extends
     private static final String BROWSER_SEARCH_TAG = "browser_search";
     private BrowserSearch mBrowserSearch;
     private View mBrowserSearchContainer;
 
     public ViewFlipper mViewFlipper;
     public ActionModeCompatView mActionBar;
     private BrowserToolbar mBrowserToolbar;
     private HomePager mHomePager;
+    private TabsPanel mTabsPanel;
     private View mHomePagerContainer;
     protected Telemetry.Timer mAboutHomeStartupTimer = null;
     private ActionModeCompat mActionMode;
     private boolean mShowActionModeEndAnimation = false;
 
     private static final int GECKO_TOOLS_MENU = -1;
     private static final int ADDON_MENU_OFFSET = 1000;
     private static class MenuItemInfo {
@@ -518,21 +520,16 @@ abstract public class BrowserApp extends
                 // Re-enable doorhanger notifications. They may trigger on the selected tab above.
                 mDoorHangerPopup.enable();
             }
         });
 
         // Intercept key events for gamepad shortcuts
         mBrowserToolbar.setOnKeyListener(this);
 
-        if (mTabsPanel != null) {
-            mTabsPanel.setTabsLayoutChangeListener(this);
-            updateSideBarState();
-        }
-
         mFindInPageBar = (FindInPageBar) findViewById(R.id.find_in_page);
         mMediaCastingBar = (MediaCastingBar) findViewById(R.id.media_casting);
 
         registerEventListener("CharEncoding:Data");
         registerEventListener("CharEncoding:State");
         registerEventListener("Feedback:LastUrl");
         registerEventListener("Feedback:OpenPlayStore");
         registerEventListener("Feedback:MaybeLater");
@@ -1033,18 +1030,22 @@ abstract public class BrowserApp extends
                 mViewFlipper.requestFocusFromTouch();
             }
         });
     }
 
     @Override
     public void refreshChrome() {
         invalidateOptionsMenu();
-        updateSideBarState();
-        mTabsPanel.refresh();
+
+        if (mTabsPanel != null) {
+            updateSideBarState();
+            mTabsPanel.refresh();
+        }
+
         mBrowserToolbar.refresh();
     }
 
     @Override
     public boolean hasTabsSideBar() {
         return (mTabsPanel != null && mTabsPanel.isSideBar());
     }
 
@@ -1154,16 +1155,27 @@ abstract public class BrowserApp extends
             } else if (event.equals("Feedback:OpenPlayStore")) {
                 Intent intent = new Intent(Intent.ACTION_VIEW);
                 intent.setData(Uri.parse("market://details?id=" + getPackageName()));
                 startActivity(intent);
             } else if (event.equals("Feedback:MaybeLater")) {
                 resetFeedbackLaunchCount();
             } else if (event.equals("Feedback:LastUrl")) {
                 getLastUrl();
+            } else if (event.equals("Gecko:DelayedStartup")) {
+                ThreadUtils.postToUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        // Force tabs panel inflation once the initial
+                        // pageload is finished.
+                        ensureTabsPanelExists();
+                    }
+                });
+
+                super.handleMessage(event, message);
             } else if (event.equals("Gecko:Ready")) {
                 // Handle this message in GeckoApp, but also enable the Settings
                 // menuitem, which is specific to BrowserApp.
                 super.handleMessage(event, message);
                 final Menu menu = mMenu;
                 ThreadUtils.postToUiThread(new Runnable() {
                     @Override
                     public void run() {
@@ -1245,21 +1257,55 @@ abstract public class BrowserApp extends
         showTabs(TabsPanel.Panel.PRIVATE_TABS);
     }
 
     @Override
     public void showRemoteTabs() {
         showTabs(TabsPanel.Panel.REMOTE_TABS);
     }
 
-    private void showTabs(TabsPanel.Panel panel) {
+    /**
+     * Ensure the TabsPanel view is properly inflated and returns
+     * true when the view has been inflated, false otherwise.
+     */
+    private boolean ensureTabsPanelExists() {
+        if (mTabsPanel != null) {
+            return false;
+        }
+
+        ViewStub tabsPanelStub = (ViewStub) findViewById(R.id.tabs_panel);
+        mTabsPanel = (TabsPanel) tabsPanelStub.inflate();
+
+        mTabsPanel.setTabsLayoutChangeListener(this);
+        updateSideBarState();
+
+        return true;
+    }
+
+    private void showTabs(final TabsPanel.Panel panel) {
         if (Tabs.getInstance().getDisplayCount() == 0)
             return;
 
-        mTabsPanel.show(panel);
+        if (ensureTabsPanelExists()) {
+            // If we've just inflated the tabs panel, only show it once the current
+            // layout pass is done to avoid displayed temporary UI states during
+            // relayout.
+            ViewTreeObserver vto = mTabsPanel.getViewTreeObserver();
+            if (vto.isAlive()) {
+                vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
+                    @Override
+                    public void onGlobalLayout() {
+                        mTabsPanel.getViewTreeObserver().removeGlobalOnLayoutListener(this);
+                        mTabsPanel.show(panel);
+                    }
+                });
+            }
+        } else {
+            mTabsPanel.show(panel);
+        }
     }
 
     @Override
     public void hideTabs() {
         mTabsPanel.hide();
     }
 
     @Override
@@ -1268,17 +1314,17 @@ abstract public class BrowserApp extends
             hideTabs();
             return true;
         }
         return false;
     }
 
     @Override
     public boolean areTabsShown() {
-        return mTabsPanel.isShown();
+        return (mTabsPanel != null && mTabsPanel.isShown());
     }
 
     @Override
     public void onTabsLayoutChange(int width, int height) {
         int animationLength = TABS_ANIMATION_DURATION;
 
         if (mMainLayoutAnimator != null) {
             animationLength = Math.max(1, animationLength - (int)mMainLayoutAnimator.getRemainingTime());
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -191,17 +191,16 @@ public abstract class GeckoApp
     private String mCurrentResponse = "";
 
     private ContactService mContactService;
     private PromptService mPromptService;
     private TextSelection mTextSelection;
 
     protected DoorHangerPopup mDoorHangerPopup;
     protected FormAssistPopup mFormAssistPopup;
-    protected TabsPanel mTabsPanel;
     protected ButtonToast mToast;
 
     protected LayerView mLayerView;
     private AbsoluteLayout mPluginContainer;
 
     private FullScreenHolder mFullScreenPluginContainer;
     private View mFullScreenPluginView;
 
@@ -1247,18 +1246,16 @@ public abstract class GeckoApp
         mOrientation = getResources().getConfiguration().orientation;
 
         setContentView(getLayout());
 
         // Set up Gecko layout.
         mGeckoLayout = (RelativeLayout) findViewById(R.id.gecko_layout);
         mMainLayout = (RelativeLayout) findViewById(R.id.main_layout);
 
-        // Set up tabs panel.
-        mTabsPanel = (TabsPanel) findViewById(R.id.tabs_panel);
         mToast = new ButtonToast(findViewById(R.id.toast));
 
         // Determine whether we should restore tabs.
         mShouldRestore = getSessionRestoreState(savedInstanceState);
         if (mShouldRestore && savedInstanceState != null) {
             boolean wasInBackground =
                 savedInstanceState.getBoolean(SAVED_STATE_IN_BACKGROUND, false);
 
--- a/mobile/android/base/resources/layout/gecko_app.xml
+++ b/mobile/android/base/resources/layout/gecko_app.xml
@@ -3,21 +3,20 @@
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                 xmlns:gecko="http://schemas.android.com/apk/res-auto"
                 android:layout_width="fill_parent"
                 android:layout_height="fill_parent">
 
-    <org.mozilla.gecko.TabsPanel android:id="@+id/tabs_panel"
-                                 android:layout_width="fill_parent"
-                                 android:layout_height="fill_parent"
-                                 android:background="@color/background_tabs"
-                                 android:visibility="invisible"/>
+    <ViewStub android:id="@+id/tabs_panel"
+              android:layout="@layout/tabs_panel_view"
+              android:layout_width="fill_parent"
+              android:layout_height="fill_parent"/>
 
    <view class="org.mozilla.gecko.GeckoApp$MainLayout"
          android:id="@+id/main_layout"
          android:layout_width="fill_parent"
          android:layout_height="fill_parent"
          android:background="@android:color/transparent">
 
         <RelativeLayout android:id="@+id/gecko_layout"
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/layout/tabs_panel_view.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<org.mozilla.gecko.TabsPanel xmlns:android="http://schemas.android.com/apk/res/android"
+                             android:id="@+id/tabs_panel"
+                             android:layout_width="fill_parent"
+                             android:layout_height="fill_parent"
+                             android:background="@color/background_tabs"/>