Bug 938205 - Factor out editing UI into ToolbarEditLayout (r=sriram)
authorLucas Rocha <lucasr@mozilla.com>
Fri, 22 Nov 2013 15:34:29 +0000
changeset 171702 0b75c809f3f7fb7637899282fd6f21e7a55e4926
parent 171701 d37c31a42f3115681786163d72e529160d375dd0
child 171703 2c699464b45cdffbd900e2ea3642539c4dd8516a
push id3224
push userlsblakk@mozilla.com
push dateTue, 04 Feb 2014 01:06:49 +0000
treeherdermozilla-beta@60c04d0987f1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssriram
bugs938205
milestone28.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 938205 - Factor out editing UI into ToolbarEditLayout (r=sriram)
mobile/android/base/moz.build
mobile/android/base/resources/layout-large-v11/browser_toolbar.xml
mobile/android/base/resources/layout/browser_toolbar.xml
mobile/android/base/resources/layout/toolbar_edit_layout.xml
mobile/android/base/toolbar/BrowserToolbar.java
mobile/android/base/toolbar/ToolbarEditLayout.java
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -289,16 +289,17 @@ gbjar.sources += [
     'toolbar/AutocompleteHandler.java',
     'toolbar/BackButton.java',
     'toolbar/BrowserToolbar.java',
     'toolbar/CanvasDelegate.java',
     'toolbar/ForwardButton.java',
     'toolbar/PageActionLayout.java',
     'toolbar/ShapedButton.java',
     'toolbar/TabCounter.java',
+    'toolbar/ToolbarEditLayout.java',
     'toolbar/ToolbarEditText.java',
     'TouchEventInterceptor.java',
     'updater/UpdateService.java',
     'updater/UpdateServiceHelper.java',
     'VideoPlayer.java',
     'WebAppAllocator.java',
     'WebAppImpl.java',
     'widget/ActivityChooserModel.java',
@@ -894,16 +895,17 @@ ANDROID_RESFILES += [
     'resources/layout/tab_menu_strip.xml',
     'resources/layout/tabs_counter.xml',
     'resources/layout/tabs_item_cell.xml',
     'resources/layout/tabs_item_row.xml',
     'resources/layout/tabs_panel.xml',
     'resources/layout/tabs_panel_header.xml',
     'resources/layout/tabs_panel_indicator.xml',
     'resources/layout/text_selection_handles.xml',
+    'resources/layout/toolbar_edit_layout.xml',
     'resources/layout/top_sites_grid_item_view.xml',
     'resources/layout/two_line_page_row.xml',
     'resources/layout/validation_message.xml',
     'resources/layout/videoplayer.xml',
     'resources/layout/web_app.xml',
     'resources/menu-large-v11/browser_app_menu.xml',
     'resources/menu-v11/browser_app_menu.xml',
     'resources/menu-xlarge-v11/browser_app_menu.xml',
--- a/mobile/android/base/resources/layout-large-v11/browser_toolbar.xml
+++ b/mobile/android/base/resources/layout-large-v11/browser_toolbar.xml
@@ -55,53 +55,24 @@
                                           android:layout_toRightOf="@id/tabs"
                                           android:layout_marginLeft="-28dp"
                                           android:layout_centerVertical="true"
                                           android:padding="13dp"
                                           android:src="@drawable/ic_menu_back"
                                           android:contentDescription="@string/back"
                                           android:background="@drawable/url_bar_nav_button"/>
 
-    <LinearLayout android:id="@+id/url_edit_container"
+    <org.mozilla.gecko.toolbar.ToolbarEditLayout android:id="@+id/edit_layout"
                   style="@style/UrlBar.Button"
                   android:paddingLeft="12dp"
                   android:paddingRight="4dp"
                   android:visibility="gone"
                   android:orientation="horizontal"
                   android:layout_toRightOf="@id/back"
-                  android:layout_toLeftOf="@id/menu_items">
-
-        <org.mozilla.gecko.toolbar.ToolbarEditText
-              android:id="@+id/url_edit_text"
-              style="@style/UrlBar.Button"
-              android:layout_width="fill_parent"
-              android:layout_height="fill_parent"
-              android:layout_weight="1.0"
-              android:hint="@string/url_bar_default_text"
-              android:textColor="@color/url_bar_title"
-              android:textColorHint="@color/url_bar_title_hint"
-              android:textColorHighlight="@color/url_bar_text_highlight"
-              android:textSelectHandle="@drawable/handle_middle"
-              android:textSelectHandleLeft="@drawable/handle_start"
-              android:textSelectHandleRight="@drawable/handle_end"
-              android:textCursorDrawable="@null"
-              android:inputType="textUri|textNoSuggestions"
-              android:imeOptions="actionGo|flagNoExtractUi|flagNoFullscreen"
-              android:selectAllOnFocus="true"
-              android:singleLine="true"
-              android:gravity="center_vertical|left"
-              gecko:autoUpdateTheme="false"/>
-
-        <ImageButton android:id="@+id/go"
-                     style="@style/UrlBar.ImageButton.Icon"
-                     android:src="@drawable/ic_url_bar_go"
-                     android:contentDescription="@string/go"
-                     android:visibility="gone"/>
-
-    </LinearLayout>
+                  android:layout_toLeftOf="@id/menu_items"/>
 
     <LinearLayout android:id="@+id/url_display_container"
                   style="@style/UrlBar.Button.Container"
                   android:layout_toRightOf="@id/back"
                   android:layout_toLeftOf="@id/menu_items">
 
         <ImageButton android:id="@+id/favicon"
                      style="@style/UrlBar.ImageButton"
--- a/mobile/android/base/resources/layout/browser_toolbar.xml
+++ b/mobile/android/base/resources/layout/browser_toolbar.xml
@@ -81,52 +81,22 @@
                         style="@style/UrlBar.ImageButton.TabCount"
                         android:layout_width="24dip"
                         android:layout_height="24dip"
                         android:layout_marginLeft="40dip"
                         android:layout_marginRight="8dip"
                         android:layout_marginTop="12dip"
                         android:layout_alignRight="@id/tabs"/>
 
-    <LinearLayout android:id="@+id/url_edit_container"
+    <org.mozilla.gecko.toolbar.ToolbarEditLayout android:id="@+id/edit_layout"
                   style="@style/UrlBar.Button"
                   android:layout_marginLeft="4dp"
                   android:layout_marginRight="4dp"
                   android:paddingLeft="8dp"
-                  android:visibility="gone"
-                  android:orientation="horizontal">
-
-        <org.mozilla.gecko.toolbar.ToolbarEditText
-              android:id="@+id/url_edit_text"
-              style="@style/UrlBar.Button"
-              android:layout_width="fill_parent"
-              android:layout_height="fill_parent"
-              android:layout_weight="1.0"
-              android:hint="@string/url_bar_default_text"
-              android:textColor="@color/url_bar_title"
-              android:textColorHint="@color/url_bar_title_hint"
-              android:textColorHighlight="@color/url_bar_text_highlight"
-              android:textSelectHandle="@drawable/handle_middle"
-              android:textSelectHandleLeft="@drawable/handle_start"
-              android:textSelectHandleRight="@drawable/handle_end"
-              android:textCursorDrawable="@null"
-              android:inputType="textUri|textNoSuggestions"
-              android:imeOptions="actionGo|flagNoExtractUi|flagNoFullscreen"
-              android:selectAllOnFocus="true"
-              android:singleLine="true"
-              android:gravity="center_vertical|left"
-              gecko:autoUpdateTheme="false"/>
-
-        <ImageButton android:id="@+id/go"
-                     style="@style/UrlBar.ImageButton.Icon"
-                     android:src="@drawable/ic_url_bar_go"
-                     android:contentDescription="@string/go"
-                     android:visibility="gone"/>
-
-    </LinearLayout>
+                  android:visibility="gone"/>
 
     <LinearLayout android:id="@+id/url_display_container"
                   style="@style/UrlBar.Button"
                   android:layout_toLeftOf="@id/tabs"
                   android:layout_marginRight="-24dp"
                   android:orientation="horizontal">
 
         <ImageButton android:id="@+id/favicon"
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/layout/toolbar_edit_layout.xml
@@ -0,0 +1,36 @@
+<?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/. -->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+       xmlns:gecko="http://schemas.android.com/apk/res-auto">
+
+        <org.mozilla.gecko.toolbar.ToolbarEditText
+              android:id="@+id/url_edit_text"
+              style="@style/UrlBar.Button"
+              android:layout_width="fill_parent"
+              android:layout_height="fill_parent"
+              android:layout_weight="1.0"
+              android:hint="@string/url_bar_default_text"
+              android:textColor="@color/url_bar_title"
+              android:textColorHint="@color/url_bar_title_hint"
+              android:textColorHighlight="@color/url_bar_text_highlight"
+              android:textSelectHandle="@drawable/handle_middle"
+              android:textSelectHandleLeft="@drawable/handle_start"
+              android:textSelectHandleRight="@drawable/handle_end"
+              android:textCursorDrawable="@null"
+              android:inputType="textUri|textNoSuggestions"
+              android:imeOptions="actionGo|flagNoExtractUi|flagNoFullscreen"
+              android:selectAllOnFocus="true"
+              android:singleLine="true"
+              android:gravity="center_vertical|left"
+              gecko:autoUpdateTheme="false"/>
+
+        <ImageButton android:id="@+id/go"
+                     style="@style/UrlBar.ImageButton.Icon"
+                     android:src="@drawable/ic_url_bar_go"
+                     android:contentDescription="@string/go"
+                     android:visibility="gone"/>
+
+</merge>
--- a/mobile/android/base/toolbar/BrowserToolbar.java
+++ b/mobile/android/base/toolbar/BrowserToolbar.java
@@ -11,27 +11,26 @@ import org.mozilla.gecko.GeckoApplicatio
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoProfile;
 import org.mozilla.gecko.LightweightTheme;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.SiteIdentityPopup;
 import org.mozilla.gecko.Tab;
 import org.mozilla.gecko.Tabs;
 import org.mozilla.gecko.animation.PropertyAnimator;
+import org.mozilla.gecko.animation.PropertyAnimator.PropertyAnimationListener;
 import org.mozilla.gecko.animation.ViewHelper;
 import org.mozilla.gecko.menu.GeckoMenu;
 import org.mozilla.gecko.menu.MenuPopup;
 import org.mozilla.gecko.PrefsHelper;
 import org.mozilla.gecko.util.Clipboard;
 import org.mozilla.gecko.util.HardwareUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 import org.mozilla.gecko.util.GeckoEventListener;
 import org.mozilla.gecko.util.StringUtils;
-import org.mozilla.gecko.toolbar.ToolbarEditText.OnTextTypeChangeListener;
-import org.mozilla.gecko.toolbar.ToolbarEditText.TextType;
 import org.mozilla.gecko.widget.GeckoImageButton;
 import org.mozilla.gecko.widget.GeckoImageView;
 import org.mozilla.gecko.widget.GeckoRelativeLayout;
 import org.mozilla.gecko.widget.GeckoTextView;
 
 import org.json.JSONObject;
 
 import android.content.Context;
@@ -101,31 +100,29 @@ public class BrowserToolbar extends Geck
         public void onStartEditing();
     }
 
     public interface OnStopEditingListener {
         public void onStopEditing();
     }
 
     private View mUrlDisplayContainer;
-    private View mUrlEditContainer;
-    private ToolbarEditText mUrlEditText;
+    private ToolbarEditLayout mUrlEditLayout;
     private View mUrlBarEntry;
     private ImageView mUrlBarRightEdge;
     private GeckoTextView mTitle;
     private int mTitlePadding;
     private boolean mSiteSecurityVisible;
     private boolean mSwitchingTabs;
     private ShapedButton mTabs;
     private ImageButton mBack;
     private ImageButton mForward;
     private ImageButton mFavicon;
     private ImageButton mStop;
     private ImageButton mSiteSecurity;
-    private ImageButton mGo;
     private PageActionLayout mPageActionLayout;
     private Animation mProgressSpinner;
     private TabCounter mTabsCounter;
     private GeckoImageButton mMenu;
     private GeckoImageView mMenuIcon;
     private LinearLayout mActionItemBar;
     private MenuPopup mMenuPopup;
     private List<? extends View> mFocusOrder;
@@ -256,19 +253,17 @@ public class BrowserToolbar extends Geck
         mShowSiteSecurity = false;
 
         mAnimatingEntry = false;
 
         mUrlBarViewOffset = res.getDimensionPixelSize(R.dimen.url_bar_offset_left);
         mDefaultForwardMargin = res.getDimensionPixelSize(R.dimen.forward_default_offset);
         mUrlDisplayContainer = findViewById(R.id.url_display_container);
         mUrlBarEntry = findViewById(R.id.url_bar_entry);
-
-        mUrlEditContainer = findViewById(R.id.url_edit_container);
-        mUrlEditText = (ToolbarEditText) findViewById(R.id.url_edit_text);
+        mUrlEditLayout = (ToolbarEditLayout) findViewById(R.id.edit_layout);
 
         // This will clip the right edge's image at 60% of its width
         mUrlBarRightEdge = (ImageView) findViewById(R.id.url_bar_right_edge);
         if (mUrlBarRightEdge != null) {
             mUrlBarRightEdge.getDrawable().setLevel(6000);
         }
 
         mTitle = (GeckoTextView) findViewById(R.id.url_bar_title);
@@ -368,42 +363,20 @@ public class BrowserToolbar extends Geck
                     menu.findItem(R.id.subscribe).setVisible(false);
                     menu.findItem(R.id.add_search_engine).setVisible(false);
                 }
 
                 menu.findItem(R.id.share).setVisible(!GeckoProfile.get(getContext()).inGuestMode());
             }
         });
 
-        mUrlEditText.setOnTextTypeChangeListener(new OnTextTypeChangeListener() {
-            @Override
-            public void onTextTypeChange(ToolbarEditText editText, TextType textType) {
-                updateGoButton(textType);
-            }
-        });
-
-        mUrlEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+        mUrlEditLayout.setOnFocusChangeListener(new View.OnFocusChangeListener() {
             @Override
             public void onFocusChange(View v, boolean hasFocus) {
-                if (v == null) {
-                    return;
-                }
-
                 setSelected(hasFocus);
-                if (hasFocus) {
-                    return;
-                }
-
-                InputMethodManager imm = (InputMethodManager) mActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
-                try {
-                    imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
-                } catch (NullPointerException e) {
-                    Log.e(LOGTAG, "InputMethodManagerService, why are you throwing"
-                                  + " a NullPointerException? See bug 782096", e);
-                }
             }
         });
 
         mTabs.setOnClickListener(new Button.OnClickListener() {
             @Override
             public void onClick(View v) {
                 toggleTabs();
             }
@@ -461,26 +434,16 @@ public class BrowserToolbar extends Geck
             public void onClick(View v) {
                 Tab tab = Tabs.getInstance().getSelectedTab();
                 if (tab != null)
                     tab.doStop();
                 setProgressVisibility(false);
             }
         });
 
-        mGo = (ImageButton) findViewById(R.id.go);
-        mGo.setOnClickListener(new Button.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                if (mCommitListener != null) {
-                    mCommitListener.onCommit();
-                }
-            }
-        });
-
         float slideWidth = getResources().getDimension(R.dimen.browser_toolbar_lock_width);
 
         LinearLayout.LayoutParams siteSecParams = (LinearLayout.LayoutParams) mSiteSecurity.getLayoutParams();
         final float scale = getResources().getDisplayMetrics().density;
         slideWidth += (siteSecParams.leftMargin + siteSecParams.rightMargin) * scale + 0.5f;
 
         mLockFadeIn = new AlphaAnimation(0.0f, 1.0f);
         mLockFadeIn.setAnimationListener(this);
@@ -529,34 +492,17 @@ public class BrowserToolbar extends Geck
             keyCode == KeyEvent.KEYCODE_DPAD_LEFT ||
             keyCode == KeyEvent.KEYCODE_DPAD_RIGHT ||
             keyCode == KeyEvent.KEYCODE_DPAD_CENTER ||
             keyCode == KeyEvent.KEYCODE_DEL ||
             keyCode == KeyEvent.KEYCODE_VOLUME_UP ||
             keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
             return false;
         } else if (isEditing()) {
-            final int prevSelStart = mUrlEditText.getSelectionStart();
-            final int prevSelEnd = mUrlEditText.getSelectionEnd();
-
-            // Manually dispatch the key event to the edit text. If selection changed as
-            // a result of the key event, then give focus back to mUrlEditText
-            mUrlEditText.dispatchKeyEvent(event);
-
-            final int curSelStart = mUrlEditText.getSelectionStart();
-            final int curSelEnd = mUrlEditText.getSelectionEnd();
-
-            if (prevSelStart != curSelStart || prevSelEnd != curSelEnd) {
-                mUrlEditText.requestFocusFromTouch();
-
-                // Restore the selection, which gets lost due to the focus switch
-                mUrlEditText.setSelection(curSelStart, curSelEnd);
-            }
-
-            return true;
+            return mUrlEditLayout.onKey(keyCode, event);
         }
 
         return false;
     }
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         // If the motion event has occured below the toolbar (due to the scroll
@@ -897,21 +843,17 @@ public class BrowserToolbar extends Geck
         }
     }
 
     public void onEditSuggestion(String suggestion) {
         if (!isEditing()) {
             return;
         }
 
-        mUrlEditText.setText(suggestion);
-        mUrlEditText.setSelection(mUrlEditText.getText().length());
-        mUrlEditText.requestFocus();
-
-        showSoftInput();
+        mUrlEditLayout.onEditSuggestion(suggestion);
     }
 
     public void setTitle(CharSequence title) {
         mTitle.setText(title);
         setContentDescription(title != null ? title : mTitle.getHint());
     }
 
     // Sets the toolbar title according to the selected tab, obeying the mShowUrl prference.
@@ -920,17 +862,17 @@ public class BrowserToolbar extends Geck
         // Keep the title unchanged if there's no selected tab, or if the tab is entering reader mode.
         if (tab == null || tab.isEnteringReaderMode()) {
             return;
         }
 
         String url = tab.getURL();
 
         if (!isEditing()) {
-            mUrlEditText.setText(url);
+            mUrlEditLayout.setText(url);
         }
 
         // Setting a null title will ensure we just see the "Enter Search or Address" placeholder text.
         if (AboutPages.isTitlelessAboutPage(url)) {
             setTitle(null);
             return;
         }
 
@@ -1036,104 +978,86 @@ public class BrowserToolbar extends Geck
     }
 
     public void setOnActivateListener(OnActivateListener listener) {
         mActivateListener = listener;
     }
 
     public void setOnCommitListener(OnCommitListener listener) {
         mCommitListener = listener;
-        mUrlEditText.setOnCommitListener(listener);
+        mUrlEditLayout.setOnCommitListener(listener);
     }
 
     public void setOnDismissListener(OnDismissListener listener) {
         mDismissListener = listener;
-        mUrlEditText.setOnDismissListener(listener);
+        mUrlEditLayout.setOnDismissListener(listener);
     }
 
     public void setOnFilterListener(OnFilterListener listener) {
         mFilterListener = listener;
-        mUrlEditText.setOnFilterListener(listener);
+        mUrlEditLayout.setOnFilterListener(listener);
     }
 
     public void setOnStartEditingListener(OnStartEditingListener listener) {
         mStartEditingListener = listener;
     }
 
     public void setOnStopEditingListener(OnStopEditingListener listener) {
         mStopEditingListener = listener;
     }
 
-    private void showSoftInput() {
-        InputMethodManager imm =
-               (InputMethodManager) mActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
-        imm.showSoftInput(mUrlEditText, InputMethodManager.SHOW_IMPLICIT);
+    private void showUrlEditLayout() {
+        setUrlEditLayoutVisibility(true, null);
     }
 
-    private void showUrlEditContainer() {
-        setUrlEditContainerVisibility(true, null);
+    private void showUrlEditLayout(PropertyAnimator animator) {
+        setUrlEditLayoutVisibility(true, animator);
+    }
+
+    private void hideUrlEditLayout() {
+        setUrlEditLayoutVisibility(false, null);
     }
 
-    private void showUrlEditContainer(PropertyAnimator animator) {
-        setUrlEditContainerVisibility(true, animator);
-    }
-
-    private void hideUrlEditContainer() {
-        setUrlEditContainerVisibility(false, null);
+    private void hideUrlEditLayout(PropertyAnimator animator) {
+        setUrlEditLayoutVisibility(false, animator);
     }
 
-    private void hideUrlEditContainer(PropertyAnimator animator) {
-        setUrlEditContainerVisibility(false, animator);
-    }
+    private void setUrlEditLayoutVisibility(final boolean showEditLayout, PropertyAnimator animator) {
+        final View viewToShow = (showEditLayout ? mUrlEditLayout : mUrlDisplayContainer);
+        final View viewToHide = (showEditLayout ? mUrlDisplayContainer : mUrlEditLayout);
 
-    private void setUrlEditContainerVisibility(final boolean showEditContainer, PropertyAnimator animator) {
-        final View viewToShow = (showEditContainer ? mUrlEditContainer : mUrlDisplayContainer);
-        final View viewToHide = (showEditContainer ? mUrlDisplayContainer : mUrlEditContainer);
+        if (showEditLayout) {
+            mUrlEditLayout.prepareShowAnimation(animator);
+        }
 
         if (animator == null) {
             viewToHide.setVisibility(View.GONE);
             viewToShow.setVisibility(View.VISIBLE);
-
-            if (showEditContainer) {
-                mUrlEditText.requestFocus();
-                showSoftInput();
-            }
-
             return;
         }
 
         ViewHelper.setAlpha(viewToShow, 0.0f);
         animator.attach(viewToShow,
                         PropertyAnimator.Property.ALPHA,
                         1.0f);
 
         animator.attach(viewToHide,
                         PropertyAnimator.Property.ALPHA,
                         0.0f);
 
-        animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() {
+        animator.addPropertyAnimationListener(new PropertyAnimationListener() {
             @Override
             public void onPropertyAnimationStart() {
                 viewToShow.setVisibility(View.VISIBLE);
-
-                if (showEditContainer) {
-                    ViewHelper.setAlpha(mGo, 0.0f);
-                    mUrlEditText.requestFocus();
-                }
             }
 
             @Override
             public void onPropertyAnimationEnd() {
                 viewToHide.setVisibility(View.GONE);
                 ViewHelper.setAlpha(viewToHide, 1.0f);
-
-                if (showEditContainer) {
-                    ViewHelper.setAlpha(mGo, 1.0f);
-                    showSoftInput();
-                }
             }
         });
     }
 
     /**
      * Disables and dims all toolbar elements which are not
      * related to editing mode.
      */
@@ -1165,51 +1089,51 @@ public class BrowserToolbar extends Geck
         if (tab != null) {
             setButtonEnabled(mBack, canDoBack(tab));
             setButtonEnabled(mForward, canDoForward(tab));
         }
     }
 
     public void setIsEditing(boolean isEditing) {
         mIsEditing = isEditing;
-        mUrlEditText.setEnabled(isEditing);
+        mUrlEditLayout.setEnabled(isEditing);
     }
 
     /**
      * Returns whether or not the URL bar is in editing mode (url bar is expanded, hiding the new
      * tab button). Note that selection state is independent of editing mode.
      */
     public boolean isEditing() {
         return mIsEditing;
     }
 
     public void startEditing(String url, PropertyAnimator animator) {
         if (isEditing()) {
             return;
         }
 
-        mUrlEditText.setText(url != null ? url : "");
+        mUrlEditLayout.setText(url != null ? url : "");
+
         setIsEditing(true);
-
         updateChildrenForEditing();
 
         if (mStartEditingListener != null) {
             mStartEditingListener.onStartEditing();
         }
 
         if (mUrlBarRightEdge != null) {
             mUrlBarRightEdge.setVisibility(View.VISIBLE);
         }
 
         final int entryTranslation = getUrlBarEntryTranslation();
         final int curveTranslation = getUrlBarCurveTranslation();
 
         // This animation doesn't make much sense in a sidebar UI
         if (HardwareUtils.isTablet() || Build.VERSION.SDK_INT < 11) {
-            showUrlEditContainer();
+            showUrlEditLayout();
 
             if (!HardwareUtils.isTablet()) {
                 if (mUrlBarRightEdge != null) {
                     ViewHelper.setTranslationX(mUrlBarRightEdge, entryTranslation);
                 }
 
                 ViewHelper.setTranslationX(mTabs, curveTranslation);
                 ViewHelper.setTranslationX(mTabsCounter, curveTranslation);
@@ -1257,17 +1181,17 @@ public class BrowserToolbar extends Geck
                             PropertyAnimator.Property.TRANSLATION_X,
                             curveTranslation);
 
             animator.attach(mMenuIcon,
                             PropertyAnimator.Property.TRANSLATION_X,
                             curveTranslation);
         }
 
-        showUrlEditContainer(animator);
+        showUrlEditLayout(animator);
 
         animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() {
             @Override
             public void onPropertyAnimationStart() {
             }
 
             @Override
             public void onPropertyAnimationEnd() {
@@ -1296,30 +1220,30 @@ public class BrowserToolbar extends Geck
         final String url = stopEditing();
         if (!TextUtils.isEmpty(url)) {
             setTitle(url);
         }
         return url;
     }
 
     private String stopEditing() {
-        final String url = mUrlEditText.getText().toString();
+        final String url = mUrlEditLayout.getText();
         if (!isEditing()) {
             return url;
         }
         setIsEditing(false);
 
         updateChildrenForEditing();
 
         if (mStopEditingListener != null) {
             mStopEditingListener.onStopEditing();
         }
 
         if (HardwareUtils.isTablet() || Build.VERSION.SDK_INT < 11) {
-            hideUrlEditContainer();
+            hideUrlEditLayout();
 
             if (!HardwareUtils.isTablet()) {
                 updateTabCountAndAnimate(Tabs.getInstance().getDisplayCount());
 
                 if (mUrlBarRightEdge != null) {
                     ViewHelper.setTranslationX(mUrlBarRightEdge, 0);
                 }
 
@@ -1362,17 +1286,17 @@ public class BrowserToolbar extends Geck
                                    PropertyAnimator.Property.TRANSLATION_X,
                                    0);
 
             contentAnimator.attach(mMenuIcon,
                                    PropertyAnimator.Property.TRANSLATION_X,
                                    0);
         }
 
-        hideUrlEditContainer(contentAnimator);
+        hideUrlEditLayout(contentAnimator);
 
         contentAnimator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() {
             @Override
             public void onPropertyAnimationStart() {
             }
 
             @Override
             public void onPropertyAnimationEnd() {
@@ -1402,39 +1326,16 @@ public class BrowserToolbar extends Geck
         });
 
         mAnimatingEntry = true;
         contentAnimator.start();
 
         return url;
     }
 
-    private void updateGoButton(TextType textType) {
-        if (textType == TextType.EMPTY) {
-            mGo.setVisibility(View.GONE);
-            return;
-        }
-
-        mGo.setVisibility(View.VISIBLE);
-
-        final int imageResource;
-        final String contentDescription;
-
-        if (textType == TextType.SEARCH_QUERY) {
-            imageResource = R.drawable.ic_url_bar_search;
-            contentDescription = mActivity.getString(R.string.search);
-        } else {
-            imageResource = R.drawable.ic_url_bar_go;
-            contentDescription = mActivity.getString(R.string.go);
-        }
-
-        mGo.setImageResource(imageResource);
-        mGo.setContentDescription(contentDescription);
-    }
-
     public void setButtonEnabled(ImageButton button, boolean enabled) {
         final Drawable drawable = button.getDrawable();
         if (drawable != null) {
             // This alpha value has to be in sync with the one used
             // in updateChildrenForEditing().
             drawable.setAlpha(enabled ? 255 : 61);
         }
 
@@ -1466,34 +1367,34 @@ public class BrowserToolbar extends Geck
                 if (!enabled) {
                     // Set the margin before the transition when hiding the forward button. We
                     // have to do this so that the favicon isn't clipped during the transition
                     ViewGroup.MarginLayoutParams layoutParams =
                         (ViewGroup.MarginLayoutParams)mUrlDisplayContainer.getLayoutParams();
                     layoutParams.leftMargin = 0;
 
                     // Do the same on the URL edit container
-                    layoutParams = (ViewGroup.MarginLayoutParams)mUrlEditContainer.getLayoutParams();
+                    layoutParams = (ViewGroup.MarginLayoutParams) mUrlEditLayout.getLayoutParams();
                     layoutParams.leftMargin = 0;
 
                     requestLayout();
                     // Note, we already translated the favicon, site security, and text field
                     // in prepareForwardAnimation, so they should appear to have not moved at
                     // all at this point.
                 }
             }
 
             @Override
             public void onPropertyAnimationEnd() {
                 if (enabled) {
                     ViewGroup.MarginLayoutParams layoutParams =
                         (ViewGroup.MarginLayoutParams)mUrlDisplayContainer.getLayoutParams();
                     layoutParams.leftMargin = mUrlBarViewOffset;
 
-                    layoutParams = (ViewGroup.MarginLayoutParams)mUrlEditContainer.getLayoutParams();
+                    layoutParams = (ViewGroup.MarginLayoutParams) mUrlEditLayout.getLayoutParams();
                     layoutParams.leftMargin = mUrlBarViewOffset;
 
                     ViewHelper.setTranslationX(mTitle, 0);
                     ViewHelper.setTranslationX(mFavicon, 0);
                     ViewHelper.setTranslationX(mSiteSecurity, 0);
                 }
 
                 ViewGroup.MarginLayoutParams layoutParams =
@@ -1583,17 +1484,17 @@ public class BrowserToolbar extends Geck
             updateForwardButton(canDoForward(tab));
 
             final boolean isPrivate = tab.isPrivate();
             setPrivateMode(isPrivate);
             mTabs.setPrivateMode(isPrivate);
             mTitle.setPrivateMode(isPrivate);
             mMenu.setPrivateMode(isPrivate);
             mMenuIcon.setPrivateMode(isPrivate);
-            mUrlEditText.setPrivateMode(isPrivate);
+            mUrlEditLayout.setPrivateMode(isPrivate);
 
             if (mBack instanceof BackButton)
                 ((BackButton) mBack).setPrivateMode(isPrivate);
 
             if (mForward instanceof ForwardButton)
                 ((ForwardButton) mForward).setPrivateMode(isPrivate);
         }
     }
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/toolbar/ToolbarEditLayout.java
@@ -0,0 +1,193 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * 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/. */
+
+package org.mozilla.gecko.toolbar;
+
+import org.mozilla.gecko.R;
+import org.mozilla.gecko.animation.PropertyAnimator;
+import org.mozilla.gecko.animation.PropertyAnimator.PropertyAnimationListener;
+import org.mozilla.gecko.animation.ViewHelper;
+import org.mozilla.gecko.toolbar.BrowserToolbar.OnCommitListener;
+import org.mozilla.gecko.toolbar.BrowserToolbar.OnDismissListener;
+import org.mozilla.gecko.toolbar.BrowserToolbar.OnFilterListener;
+import org.mozilla.gecko.toolbar.ToolbarEditText.OnTextTypeChangeListener;
+import org.mozilla.gecko.toolbar.ToolbarEditText.TextType;
+import org.mozilla.gecko.widget.GeckoLinearLayout;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnFocusChangeListener;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.ImageButton;
+
+public class ToolbarEditLayout extends GeckoLinearLayout {
+
+    private final ToolbarEditText mEditText;
+    private final ImageButton mGo;
+
+    private OnCommitListener mCommitListener;
+    private OnFocusChangeListener mFocusChangeListener;
+
+    public ToolbarEditLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        setOrientation(HORIZONTAL);
+
+        LayoutInflater.from(context).inflate(R.layout.toolbar_edit_layout, this);
+        mGo = (ImageButton) findViewById(R.id.go);
+        mEditText = (ToolbarEditText) findViewById(R.id.url_edit_text);
+    }
+
+    @Override
+    public void onAttachedToWindow() {
+        mGo.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (mCommitListener != null) {
+                    mCommitListener.onCommit();
+                }
+            }
+        });
+
+        mEditText.setOnTextTypeChangeListener(new OnTextTypeChangeListener() {
+            @Override
+            public void onTextTypeChange(ToolbarEditText editText, TextType textType) {
+                updateGoButton(textType);
+            }
+        });
+
+        mEditText.setOnFocusChangeListener(new OnFocusChangeListener() {
+            @Override
+            public void onFocusChange(View v, boolean hasFocus) {
+                if (mFocusChangeListener != null) {
+                    mFocusChangeListener.onFocusChange(ToolbarEditLayout.this, hasFocus);
+                }
+            }
+        });
+    }
+
+    @Override
+    public void setOnFocusChangeListener(OnFocusChangeListener listener) {
+        mFocusChangeListener = listener;
+    }
+
+    @Override
+    public void setEnabled(boolean enabled) {
+        super.setEnabled(enabled);
+
+        mGo.setEnabled(enabled);
+        mEditText.setEnabled(enabled);
+    }
+
+    @Override
+    public void setPrivateMode(boolean isPrivate) {
+        super.setPrivateMode(isPrivate);
+        mEditText.setPrivateMode(isPrivate);
+    }
+
+    private void updateGoButton(TextType textType) {
+        if (textType == TextType.EMPTY) {
+            mGo.setVisibility(View.GONE);
+            return;
+        }
+
+        mGo.setVisibility(View.VISIBLE);
+
+        final int imageResource;
+        final String contentDescription;
+
+        if (textType == TextType.SEARCH_QUERY) {
+            imageResource = R.drawable.ic_url_bar_search;
+            contentDescription = getContext().getString(R.string.search);
+        } else {
+            imageResource = R.drawable.ic_url_bar_go;
+            contentDescription = getContext().getString(R.string.go);
+        }
+
+        mGo.setImageResource(imageResource);
+        mGo.setContentDescription(contentDescription);
+    }
+
+    private void showSoftInput() {
+        InputMethodManager imm =
+               (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+        imm.showSoftInput(mEditText, InputMethodManager.SHOW_IMPLICIT);
+    }
+
+    void prepareShowAnimation(PropertyAnimator animator) {
+        if (animator == null) {
+            mEditText.requestFocus();
+            showSoftInput();
+            return;
+        }
+
+        animator.addPropertyAnimationListener(new PropertyAnimationListener() {
+            @Override
+            public void onPropertyAnimationStart() {
+                ViewHelper.setAlpha(mGo, 0.0f);
+                mEditText.requestFocus();
+            }
+
+            @Override
+            public void onPropertyAnimationEnd() {
+                ViewHelper.setAlpha(mGo, 1.0f);
+                showSoftInput();
+            }
+        });
+    }
+
+    void setOnCommitListener(OnCommitListener listener) {
+        mCommitListener = listener;
+        mEditText.setOnCommitListener(listener);
+    }
+
+    void setOnDismissListener(OnDismissListener listener) {
+        mEditText.setOnDismissListener(listener);
+    }
+
+    void setOnFilterListener(OnFilterListener listener) {
+        mEditText.setOnFilterListener(listener);
+    }
+
+    boolean onKey(int keyCode, KeyEvent event) {
+        final int prevSelStart = mEditText.getSelectionStart();
+        final int prevSelEnd = mEditText.getSelectionEnd();
+
+        // Manually dispatch the key event to the edit text. If selection changed as
+        // a result of the key event, then give focus back to mEditText
+        mEditText.dispatchKeyEvent(event);
+
+        final int curSelStart = mEditText.getSelectionStart();
+        final int curSelEnd = mEditText.getSelectionEnd();
+
+        if (prevSelStart != curSelStart || prevSelEnd != curSelEnd) {
+            mEditText.requestFocusFromTouch();
+
+            // Restore the selection, which gets lost due to the focus switch
+            mEditText.setSelection(curSelStart, curSelEnd);
+        }
+
+        return true;
+    }
+
+    void onEditSuggestion(String suggestion) {
+        mEditText.setText(suggestion);
+        mEditText.setSelection(mEditText.getText().length());
+        mEditText.requestFocus();
+
+        showSoftInput();
+    }
+
+    void setText(String text) {
+        mEditText.setText(text);
+    }
+
+    String getText() {
+        return mEditText.getText().toString();
+    }
+}
\ No newline at end of file