Bug 796886 - Tapping anywhere in main layout should close tabs panel on phones (r=mfinkle)
authorLucas Rocha <lucasr@mozilla.com>
Sat, 13 Oct 2012 00:23:20 +0100
changeset 110399 4e3c9bd6bd60783bd927eb7214c99f7807c83750
parent 110398 bbcf489988a470c5b41dc207b7b6ad6c9db3bd4c
child 110400 d16548ef9acccf64c85c32195f76a3a3aa11ecf2
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
reviewersmfinkle
bugs796886
milestone19.0a1
Bug 796886 - Tapping anywhere in main layout should close tabs panel on phones (r=mfinkle)
mobile/android/base/AboutHomeContent.java.in
mobile/android/base/BrowserApp.java
mobile/android/base/GeckoApp.java
mobile/android/base/GeckoViewsFactory.java
mobile/android/base/resources/layout-xlarge-v11/gecko_app.xml.in
mobile/android/base/resources/layout/gecko_app.xml.in
--- a/mobile/android/base/AboutHomeContent.java.in
+++ b/mobile/android/base/AboutHomeContent.java.in
@@ -89,17 +89,16 @@ public class AboutHomeContent extends Sc
 
     private AboutHomePromoBox mPromoBox;
     private AboutHomePromoBox.Type mPrelimPromoBoxType;
     protected AboutHomeSection mAddons;
     protected AboutHomeSection mLastTabs;
     protected AboutHomeSection mRemoteTabs;
 
     private View.OnClickListener mRemoteTabClickListener;
-    private OnInterceptTouchListener mOnInterceptTouchListener;
 
     public interface UriLoadCallback {
         public void callback(String uriSpec);
     }
 
     public interface VoidCallback {
         public void callback();
     }
@@ -342,34 +341,16 @@ public class AboutHomeContent extends Sc
             mTopSitesAdapter.notifyDataSetChanged();
 
         removeAllViews(); // We must remove the currently inflated view to allow for reinflation.
         inflate();
         mTopSitesGrid.setAdapter(mTopSitesAdapter); // mTopSitesGrid is a new instance (from loadTopSites()).
         update(AboutHomeContent.UpdateFlags.ALL); // Refresh all elements.
     }
 
-    @Override
-    public boolean onInterceptTouchEvent(MotionEvent event) {
-        if (mOnInterceptTouchListener != null && mOnInterceptTouchListener.onInterceptTouchEvent(this, event))
-            return true;
-        return super.onInterceptTouchEvent(event);
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        if (mOnInterceptTouchListener != null && mOnInterceptTouchListener.onTouch(this, event))
-            return true;
-        return super.onTouchEvent(event);
-    }
-
-    public void setOnInterceptTouchListener(OnInterceptTouchListener listener) {
-        mOnInterceptTouchListener = listener;
-    }
-
     private String readFromZipFile(String filename) {
         ZipFile zip = null;
         String str = null;
         try {
             InputStream fileStream = null;
             File applicationPackage = new File(mActivity.getApplication().getPackageResourcePath());
             zip = new ZipFile(applicationPackage);
             if (zip == null)
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -19,24 +19,26 @@ import android.app.AlertDialog;
 import android.content.DialogInterface;
 import android.content.SharedPreferences;
 import android.content.Intent;
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.animation.Interpolator;
 import android.widget.LinearLayout;
 import android.widget.Toast;
 
 import dalvik.system.DexClassLoader;
 
@@ -203,16 +205,18 @@ abstract public class BrowserApp extends
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         LinearLayout actionBar = (LinearLayout) getActionBarLayout();
         mMainLayout.addView(actionBar, 0);
 
+        ((GeckoApp.MainLayout) mMainLayout).setOnInterceptTouchListener(new HideTabsTouchListener());
+
         mBrowserToolbar = new BrowserToolbar(this);
         mBrowserToolbar.from(actionBar);
 
         if (mTabsPanel != null)
             mTabsPanel.setTabsLayoutChangeListener(this);
 
         mFindInPageBar = (FindInPageBar) findViewById(R.id.find_in_page);
 
@@ -670,28 +674,72 @@ abstract public class BrowserApp extends
                             Tabs.getInstance().loadUrl(url);
                         }
                     });
                     mAboutHomeContent.setLoadCompleteCallback(new AboutHomeContent.VoidCallback() {
                          public void callback() {
                              mAboutHomeStartupTimer.stop();
                          }
                     });
-                    mAboutHomeContent.setOnInterceptTouchListener(new ContentTouchListener());
                 } else {
                     mAboutHomeContent.update(EnumSet.of(AboutHomeContent.UpdateFlags.TOP_SITES,
                                                         AboutHomeContent.UpdateFlags.REMOTE_TABS));
                 }
                 mAboutHomeContent.setVisibility(View.VISIBLE);
             } else {
                 findViewById(R.id.abouthome_content).setVisibility(View.GONE);
             }
         } 
     }
 
+    private class HideTabsTouchListener implements OnInterceptTouchListener {
+        private boolean mIsHidingTabs = false;
+
+        @Override
+        public boolean onInterceptTouchEvent(View view, MotionEvent event) {
+            // We need to account for scroll state for the touched view otherwise
+            // tapping on an "empty" part of the view will still be considered a
+            // valid touch event.
+            if (view.getScrollX() != 0 || view.getScrollY() != 0) {
+                Rect rect = new Rect();
+                view.getHitRect(rect);
+                rect.offset(-view.getScrollX(), -view.getScrollY());
+
+                int[] viewCoords = new int[2];
+                view.getLocationOnScreen(viewCoords);
+
+                int x = (int) event.getRawX() - viewCoords[0];
+                int y = (int) event.getRawY() - viewCoords[1];
+
+                if (!rect.contains(x, y))
+                    return false;
+            }
+
+            // If the tab tray is showing, hide the tab tray and don't send the event to content.
+            if (event.getActionMasked() == MotionEvent.ACTION_DOWN && autoHideTabs()) {
+                mIsHidingTabs = true;
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public boolean onTouch(View view, MotionEvent event) {
+            if (mIsHidingTabs) {
+                // Keep consuming events until the gesture finishes.
+                int action = event.getActionMasked();
+                if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+                    mIsHidingTabs = false;
+                }
+                return true;
+            }
+            return false;
+        }
+    }
+
     private void addAddonMenuItem(final int id, final String label, final String icon) {
         if (mMenu == null) {
             if (mAddonMenuItemsCache == null)
                 mAddonMenuItemsCache = new Vector<MenuItemInfo>();
 
             mAddonMenuItemsCache.add(new MenuItemInfo(id, label, icon));
             return;
         }
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -2534,27 +2534,29 @@ abstract public class GeckoApp
 
     public void notifyCheckUpdateResult(boolean result) {
         GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Update:CheckResult", result ? "true" : "false"));
     }
 
     protected void connectGeckoLayerClient() {
         mLayerView.getLayerClient().notifyGeckoReady();
 
-        mLayerView.getTouchEventHandler().setOnTouchListener(new ContentTouchListener() {
+        mLayerView.getTouchEventHandler().setOnTouchListener(new OnInterceptTouchListener() {
             private PointF initialPoint = null;
 
             @Override
+            public boolean onInterceptTouchEvent(View view, MotionEvent event) {
+                return false;
+            }
+
+            @Override
             public boolean onTouch(View view, MotionEvent event) {
                 if (event == null)
                     return true;
 
-                if (super.onTouch(view, event))
-                    return true;
-
                 int action = event.getAction();
                 PointF point = new PointF(event.getX(), event.getY());
                 if ((action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
                     initialPoint = point;
                 }
 
                 if (initialPoint != null && (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_MOVE) {
                     if (PointUtils.subtract(point, initialPoint).length() < PanZoomController.PAN_THRESHOLD) {
@@ -2567,40 +2569,40 @@ abstract public class GeckoApp
                 }
 
                 GeckoAppShell.sendEventToGecko(GeckoEvent.createMotionEvent(event));
                 return true;
             }
         });
     }
 
-    protected class ContentTouchListener implements OnInterceptTouchListener {
-        private boolean mIsHidingTabs = false;
-
-        @Override
-        public boolean onInterceptTouchEvent(View view, MotionEvent event) {
-            // If the tab tray is showing, hide the tab tray and don't send the event to content.
-            if (event.getActionMasked() == MotionEvent.ACTION_DOWN && autoHideTabs()) {
-                mIsHidingTabs = true;
-                return true;
-            }
-            return false;
+    public static class MainLayout extends LinearLayout {
+        private OnInterceptTouchListener mOnInterceptTouchListener;
+
+        public MainLayout(Context context, AttributeSet attrs) {
+            super(context, attrs);
+            mOnInterceptTouchListener = null;
+        }
+
+        public void setOnInterceptTouchListener(OnInterceptTouchListener listener) {
+            mOnInterceptTouchListener = listener;
         }
 
         @Override
-        public boolean onTouch(View view, MotionEvent event) {
-            if (mIsHidingTabs) {
-                // Keep consuming events until the gesture finishes.
-                int action = event.getActionMasked();
-                if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
-                    mIsHidingTabs = false;
-                }
+        public boolean onInterceptTouchEvent(MotionEvent event) {
+            if (mOnInterceptTouchListener != null && mOnInterceptTouchListener.onInterceptTouchEvent(this, event))
                 return true;
-            }
-            return false;
+            return super.onInterceptTouchEvent(event);
+        }
+
+        @Override
+        public boolean onTouchEvent(MotionEvent event) {
+            if (mOnInterceptTouchListener != null && mOnInterceptTouchListener.onTouch(this, event))
+                return true;
+            return super.onTouchEvent(event);
         }
     }
 
     public boolean linkerExtract() {
         return false;
     }
 
     private class FullScreenHolder extends FrameLayout {
--- a/mobile/android/base/GeckoViewsFactory.java
+++ b/mobile/android/base/GeckoViewsFactory.java
@@ -47,16 +47,18 @@ public final class GeckoViewsFactory imp
             else if (TextUtils.equals(viewName, "AboutHomeSection"))
                 return new AboutHomeSection(context, attrs);
             else if (TextUtils.equals(viewName, "AwesomeBarTabs"))
                 return new AwesomeBarTabs(context, attrs);
             else if (TextUtils.equals(viewName, "BrowserToolbarBackground"))
                 return new BrowserToolbarBackground(context, attrs);
             else if (TextUtils.equals(viewName, "FormAssistPopup"))
                 return new FormAssistPopup(context, attrs);
+            else if (TextUtils.equals(viewName, "GeckoApp$MainLayout"))
+                return new GeckoApp.MainLayout(context, attrs);
             else if (TextUtils.equals(viewName, "LinkTextView"))
                 return new LinkTextView(context, attrs);
             else if (TextUtils.equals(viewName, "FindInPageBar"))
                 return new FindInPageBar(context, attrs);
             else if (TextUtils.equals(viewName, "MenuButton"))
                 return new MenuButton(context, attrs);
             else if (TextUtils.equals(viewName, "TabsButton"))
                 return new TabsButton(context, attrs);
--- a/mobile/android/base/resources/layout-xlarge-v11/gecko_app.xml.in
+++ b/mobile/android/base/resources/layout-xlarge-v11/gecko_app.xml.in
@@ -10,18 +10,19 @@
                 android:layout_height="fill_parent">
 
    <org.mozilla.gecko.TabsPanel android:id="@+id/tabs_panel"
                                 android:layout_width="200dip"
                                 android:layout_height="fill_parent"
                                 android:background="@drawable/tabs_tray_bg_repeat"
                                 gecko:sidebar="true"/>
 
-   <LinearLayout android:id="@+id/main_layout"
-                 style="@style/Screen.Transparent">
+   <view class="org.mozilla.gecko.GeckoApp$MainLayout"
+         android:id="@+id/main_layout"
+         style="@style/Screen.Transparent">
  
         <!-- BrowserToolbar will be added dynamically -->
         
         <RelativeLayout android:id="@+id/gecko_layout"
                         android:layout_width="fill_parent"
                         android:layout_height="fill_parent"
                         android:layout_weight="1">
 
@@ -36,11 +37,11 @@
 
         <org.mozilla.gecko.FindInPageBar android:id="@+id/find_in_page"
                                          android:layout_width="fill_parent"
                                          android:layout_height="wrap_content"
                                          android:layout_gravity="bottom"
                                          style="@style/FindBar"
                                          android:visibility="gone"/>
 
-    </LinearLayout>
+    </view>
 
 </RelativeLayout>
--- a/mobile/android/base/resources/layout/gecko_app.xml.in
+++ b/mobile/android/base/resources/layout/gecko_app.xml.in
@@ -10,18 +10,19 @@
                 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="@drawable/tabs_tray_bg_repeat"
                                 gecko:sidebar="false"/>
 
-   <LinearLayout android:id="@+id/main_layout"
-                 style="@style/Screen.Transparent">
+   <view class="org.mozilla.gecko.GeckoApp$MainLayout"
+         android:id="@+id/main_layout"
+         style="@style/Screen.Transparent">
  
         <!-- BrowserToolbar will be added dynamically -->
 
         <RelativeLayout android:id="@+id/gecko_layout"
                         android:layout_width="fill_parent"
                         android:layout_height="fill_parent"
                         android:layout_weight="1">
 
@@ -36,11 +37,11 @@
 
         <org.mozilla.gecko.FindInPageBar android:id="@+id/find_in_page"
                                          android:layout_width="fill_parent"
                                          android:layout_height="wrap_content"
                                          android:layout_gravity="bottom"
                                          style="@style/FindBar"
                                          android:visibility="gone"/>
 
-    </LinearLayout>
+    </view>
 
 </RelativeLayout>