Bug 1366668 - Part 1: Duplicates awesome bar resources. r?nechen,walkingice draft
authorJing-wei Wu <topwu.tw@gmail.com>
Tue, 01 Aug 2017 09:55:48 +0800
changeset 621003 5e57f82143f8d8bd08a66dbb12a9a286d56ec6e4
parent 621002 cf67e8c26b31e945a83c4c890486efe043a55471
child 621004 27a61a802b6c127076c075880cbe3c16f3d2c20b
push id72220
push userbmo:topwu.tw@gmail.com
push dateFri, 04 Aug 2017 06:56:19 +0000
reviewersnechen, walkingice
bugs1366668
milestone57.0a1
Bug 1366668 - Part 1: Duplicates awesome bar resources. r?nechen,walkingice MozReview-Commit-ID: 7tY3ugTATxh
mobile/android/app/src/australis/java/org/mozilla/gecko/toolbar/BrowserToolbarTablet.java
mobile/android/app/src/australis/java/org/mozilla/gecko/toolbar/BrowserToolbarTabletBase.java
mobile/android/app/src/australis/java/org/mozilla/gecko/toolbar/ForwardButton.java
mobile/android/app/src/australis/java/org/mozilla/gecko/toolbar/ToolbarDisplayLayout.java
mobile/android/app/src/australis/res/color/action_bar_menu_item_colors.xml
mobile/android/app/src/australis/res/color/tab_new_tab_strip_colors.xml
mobile/android/app/src/australis/res/drawable-large-v11/url_bar_nav_button.xml
mobile/android/app/src/main/res/color/action_bar_menu_item_colors.xml
mobile/android/app/src/main/res/color/tab_new_tab_strip_colors.xml
mobile/android/app/src/main/res/drawable-large-v11/url_bar_nav_button.xml
mobile/android/app/src/photon/java/org/mozilla/gecko/toolbar/BrowserToolbarTablet.java
mobile/android/app/src/photon/java/org/mozilla/gecko/toolbar/BrowserToolbarTabletBase.java
mobile/android/app/src/photon/java/org/mozilla/gecko/toolbar/ForwardButton.java
mobile/android/app/src/photon/java/org/mozilla/gecko/toolbar/ToolbarDisplayLayout.java
mobile/android/app/src/photon/res/color/action_bar_menu_item_colors.xml
mobile/android/app/src/photon/res/color/tab_new_tab_strip_colors.xml
mobile/android/app/src/photon/res/drawable-large-v11/url_bar_nav_button.xml
mobile/android/base/java/org/mozilla/gecko/toolbar/BrowserToolbarTablet.java
mobile/android/base/java/org/mozilla/gecko/toolbar/BrowserToolbarTabletBase.java
mobile/android/base/java/org/mozilla/gecko/toolbar/ForwardButton.java
mobile/android/base/java/org/mozilla/gecko/toolbar/ToolbarDisplayLayout.java
rename from mobile/android/base/java/org/mozilla/gecko/toolbar/BrowserToolbarTablet.java
rename to mobile/android/app/src/australis/java/org/mozilla/gecko/toolbar/BrowserToolbarTablet.java
rename from mobile/android/base/java/org/mozilla/gecko/toolbar/BrowserToolbarTabletBase.java
rename to mobile/android/app/src/australis/java/org/mozilla/gecko/toolbar/BrowserToolbarTabletBase.java
rename from mobile/android/base/java/org/mozilla/gecko/toolbar/ForwardButton.java
rename to mobile/android/app/src/australis/java/org/mozilla/gecko/toolbar/ForwardButton.java
rename from mobile/android/base/java/org/mozilla/gecko/toolbar/ToolbarDisplayLayout.java
rename to mobile/android/app/src/australis/java/org/mozilla/gecko/toolbar/ToolbarDisplayLayout.java
rename from mobile/android/app/src/main/res/color/action_bar_menu_item_colors.xml
rename to mobile/android/app/src/australis/res/color/action_bar_menu_item_colors.xml
new file mode 100644
--- /dev/null
+++ b/mobile/android/app/src/australis/res/color/tab_new_tab_strip_colors.xml
@@ -0,0 +1,14 @@
+<?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/. -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+          xmlns:gecko="http://schemas.android.com/apk/res-auto">
+
+    <item gecko:state_light="true"
+          android:color="@color/toolbar_icon_grey"/>
+
+    <item android:color="@color/tabs_tray_icon_grey"/>
+
+</selector>
\ No newline at end of file
rename from mobile/android/app/src/main/res/drawable-large-v11/url_bar_nav_button.xml
rename to mobile/android/app/src/australis/res/drawable-large-v11/url_bar_nav_button.xml
deleted file mode 100644
--- a/mobile/android/app/src/main/res/color/tab_new_tab_strip_colors.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-<?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/. -->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:gecko="http://schemas.android.com/apk/res-auto">
-
-    <item gecko:state_light="true"
-          android:color="@color/toolbar_icon_grey"/>
-
-    <item android:color="@color/tabs_tray_icon_grey"/>
-
-</selector>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/mobile/android/app/src/photon/java/org/mozilla/gecko/toolbar/BrowserToolbarTablet.java
@@ -0,0 +1,211 @@
+/* -*- 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.ViewHelper;
+import org.mozilla.gecko.util.ViewUtil;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.support.v4.view.MarginLayoutParamsCompat;
+import android.support.v4.view.ViewCompat;
+import android.util.AttributeSet;
+
+/**
+ * The toolbar implementation for tablet.
+ */
+class BrowserToolbarTablet extends BrowserToolbarTabletBase {
+
+    private static final int FORWARD_ANIMATION_DURATION = 450;
+
+    private enum ForwardButtonState {
+        HIDDEN,
+        DISPLAYED,
+        TRANSITIONING,
+    }
+
+    private final int forwardButtonTranslationWidth;
+
+    private ForwardButtonState forwardButtonState;
+
+    private boolean backButtonWasEnabledOnStartEditing;
+
+    public BrowserToolbarTablet(final Context context, final AttributeSet attrs) {
+        super(context, attrs);
+
+        forwardButtonTranslationWidth =
+                getResources().getDimensionPixelOffset(R.dimen.tablet_nav_button_width);
+
+        // The forward button is initially expanded (in the layout file)
+        // so translate it for start of the expansion animation; future
+        // iterations translate it to this position when hiding and will already be set up.
+        ViewHelper.setTranslationX(forwardButton, forwardButtonTranslationWidth * (isLayoutRtl() ? 1 : -1));
+
+        // TODO: Move this to *TabletBase when old tablet is removed.
+        // We don't want users clicking the forward button in transitions, but we don't want it to
+        // look disabled to avoid flickering complications (e.g. disabled in editing mode), so undo
+        // the work of the super class' constructor.
+        forwardButton.setEnabled(true);
+
+        updateForwardButtonState(ForwardButtonState.HIDDEN);
+    }
+
+    private boolean isLayoutRtl() {
+        return ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL;
+    }
+
+    private void updateForwardButtonState(final ForwardButtonState state) {
+        forwardButtonState = state;
+        forwardButton.setEnabled(forwardButtonState == ForwardButtonState.DISPLAYED);
+    }
+
+    @Override
+    public boolean isAnimating() {
+        return false;
+    }
+
+    @Override
+    protected void triggerStartEditingTransition(final PropertyAnimator animator) {
+        showUrlEditLayout();
+    }
+
+    @Override
+    protected void triggerStopEditingTransition() {
+        hideUrlEditLayout();
+    }
+
+    @Override
+    protected void animateForwardButton(final ForwardButtonAnimation animation) {
+        final boolean willShowForward = (animation == ForwardButtonAnimation.SHOW);
+        if ((forwardButtonState != ForwardButtonState.HIDDEN && willShowForward) ||
+                (forwardButtonState != ForwardButtonState.DISPLAYED && !willShowForward)) {
+            return;
+        }
+        updateForwardButtonState(ForwardButtonState.TRANSITIONING);
+
+        // We want the forward button to show immediately when switching tabs
+        final PropertyAnimator forwardAnim =
+                new PropertyAnimator(isSwitchingTabs ? 10 : FORWARD_ANIMATION_DURATION);
+
+        forwardAnim.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() {
+            @Override
+            public void onPropertyAnimationStart() {
+                if (!willShowForward) {
+                    // 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
+                    MarginLayoutParams layoutParams = (MarginLayoutParams) urlDisplayLayout.getLayoutParams();
+                    ViewUtil.setMarginStart(layoutParams, 0, isLayoutRtl());
+
+                    // Do the same on the URL edit container
+                    layoutParams = (MarginLayoutParams) urlEditLayout.getLayoutParams();
+                    ViewUtil.setMarginStart(layoutParams, 0, isLayoutRtl());
+
+                    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() {
+                final ForwardButtonState newForwardButtonState;
+                if (willShowForward) {
+                    // Increase the margins to ensure the text does not run outside the View.
+                    MarginLayoutParams layoutParams = (MarginLayoutParams) urlDisplayLayout.getLayoutParams();
+                    ViewUtil.setMarginStart(layoutParams, forwardButtonTranslationWidth, isLayoutRtl());
+
+                    layoutParams = (MarginLayoutParams) urlEditLayout.getLayoutParams();
+                    ViewUtil.setMarginStart(layoutParams, forwardButtonTranslationWidth, isLayoutRtl());
+
+                    newForwardButtonState = ForwardButtonState.DISPLAYED;
+                } else {
+                    newForwardButtonState = ForwardButtonState.HIDDEN;
+                }
+
+                urlDisplayLayout.finishForwardAnimation();
+                updateForwardButtonState(newForwardButtonState);
+
+                requestLayout();
+            }
+        });
+
+        prepareForwardAnimation(forwardAnim, animation, forwardButtonTranslationWidth);
+        forwardAnim.start();
+    }
+
+    private void prepareForwardAnimation(PropertyAnimator anim, ForwardButtonAnimation animation, int width) {
+        boolean isLayoutRtl = isLayoutRtl();
+        if (animation == ForwardButtonAnimation.HIDE) {
+            anim.attach(forwardButton,
+                      PropertyAnimator.Property.TRANSLATION_X,
+                      width * (isLayoutRtl ? 1 : -1));
+            anim.attach(forwardButton,
+                      PropertyAnimator.Property.ALPHA,
+                      0);
+
+        } else {
+            anim.attach(forwardButton,
+                      PropertyAnimator.Property.TRANSLATION_X,
+                      0);
+            anim.attach(forwardButton,
+                      PropertyAnimator.Property.ALPHA,
+                      1);
+        }
+
+        urlDisplayLayout.prepareForwardAnimation(anim, animation, width);
+    }
+
+    @Override
+    public void triggerTabsPanelTransition(final PropertyAnimator animator, final boolean areTabsShown) {
+        // Do nothing.
+    }
+
+    @Override
+    public void startEditing(final String url, final PropertyAnimator animator) {
+        // We already know the forward button state - no need to store it here.
+        backButtonWasEnabledOnStartEditing = backButton.isEnabled();
+
+        backButton.setEnabled(false);
+        forwardButton.setEnabled(false);
+
+        super.startEditing(url, animator);
+    }
+
+    @Override
+    public String commitEdit() {
+        stopEditingNewTablet();
+        return super.commitEdit();
+    }
+
+    @Override
+    public String cancelEdit() {
+        // This can get called when we're not editing but we only want
+        // to make these changes when leaving editing mode.
+        if (isEditing()) {
+            stopEditingNewTablet();
+
+            backButton.setEnabled(backButtonWasEnabledOnStartEditing);
+            updateForwardButtonState(forwardButtonState);
+        }
+
+        return super.cancelEdit();
+    }
+
+    private void stopEditingNewTablet() {
+        // Undo the changes caused by calling setEnabled for forwardButton in startEditing.
+        // Note that this should be called first so the enabled state of the
+        // forward button is set to the proper value.
+        forwardButton.setEnabled(true);
+    }
+
+    @Override
+    protected Drawable getLWTDefaultStateSetDrawable() {
+        return BrowserToolbar.getLightweightThemeDrawable(this, getTheme(), R.color.toolbar_grey);
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/app/src/photon/java/org/mozilla/gecko/toolbar/BrowserToolbarTabletBase.java
@@ -0,0 +1,203 @@
+/* -*- 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 java.util.Arrays;
+
+import android.support.v4.content.ContextCompat;
+import org.mozilla.gecko.R;
+import org.mozilla.gecko.Tab;
+import org.mozilla.gecko.Tabs;
+import org.mozilla.gecko.skin.SkinConfig;
+import org.mozilla.gecko.tabs.TabHistoryController;
+import org.mozilla.gecko.menu.MenuItemActionBar;
+import org.mozilla.gecko.util.HardwareUtils;
+import org.mozilla.gecko.widget.themed.ThemedFrameLayout;
+import org.mozilla.gecko.widget.themed.ThemedImageButton;
+import org.mozilla.gecko.widget.themed.ThemedTextView;
+
+import android.content.Context;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+
+/**
+ * A base implementations of the browser toolbar for tablets.
+ * This class manages any Views, variables, etc. that are exclusive to tablet.
+ */
+abstract class BrowserToolbarTabletBase extends BrowserToolbar {
+
+    protected enum ForwardButtonAnimation {
+        SHOW,
+        HIDE
+    }
+
+    protected final LinearLayout actionItemBar;
+
+    protected final BackButton backButton;
+    protected final ForwardButton forwardButton;
+
+    protected final View menuButtonMarginView;
+
+    private final PorterDuffColorFilter privateBrowsingTabletMenuItemColorFilter;
+
+    protected abstract void animateForwardButton(ForwardButtonAnimation animation);
+
+    public BrowserToolbarTabletBase(final Context context, final AttributeSet attrs) {
+        super(context, attrs);
+
+        actionItemBar = (LinearLayout) findViewById(R.id.menu_items);
+
+        backButton = (BackButton) findViewById(R.id.back);
+        backButton.setEnabled(false);
+        forwardButton = (ForwardButton) findViewById(R.id.forward);
+        forwardButton.setEnabled(false);
+        initButtonListeners();
+
+        focusOrder.addAll(Arrays.asList(tabsButton, (View) backButton, (View) forwardButton, this));
+        focusOrder.addAll(urlDisplayLayout.getFocusOrder());
+        focusOrder.addAll(Arrays.asList(actionItemBar, menuButton));
+
+        urlDisplayLayout.updateSiteIdentityAnchor(backButton);
+
+        privateBrowsingTabletMenuItemColorFilter = new PorterDuffColorFilter(
+                ContextCompat.getColor(context, R.color.tabs_tray_icon_grey), PorterDuff.Mode.SRC_IN);
+
+        menuButtonMarginView = findViewById(R.id.menu_margin);
+        if (menuButtonMarginView != null) {
+            menuButtonMarginView.setVisibility(View.VISIBLE);
+        }
+    }
+
+    private void initButtonListeners() {
+        backButton.setOnClickListener(new Button.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                Tabs.getInstance().getSelectedTab().doBack();
+            }
+        });
+        backButton.setOnLongClickListener(new Button.OnLongClickListener() {
+            @Override
+            public boolean onLongClick(View view) {
+                return tabHistoryController.showTabHistory(Tabs.getInstance().getSelectedTab(),
+                        TabHistoryController.HistoryAction.BACK);
+            }
+        });
+
+        forwardButton.setOnClickListener(new Button.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                Tabs.getInstance().getSelectedTab().doForward();
+            }
+        });
+        forwardButton.setOnLongClickListener(new Button.OnLongClickListener() {
+            @Override
+            public boolean onLongClick(View view) {
+                return tabHistoryController.showTabHistory(Tabs.getInstance().getSelectedTab(),
+                        TabHistoryController.HistoryAction.FORWARD);
+            }
+        });
+    }
+
+    @Override
+    protected boolean isTabsButtonOffscreen() {
+        return false;
+    }
+
+    @Override
+    public boolean addActionItem(final View actionItem) {
+        actionItemBar.addView(actionItem, LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
+        return true;
+    }
+
+    @Override
+    public void removeActionItem(final View actionItem) {
+        actionItemBar.removeView(actionItem);
+    }
+
+    @Override
+    protected void updateNavigationButtons(final Tab tab) {
+        backButton.setEnabled(canDoBack(tab));
+        animateForwardButton(
+                canDoForward(tab) ? ForwardButtonAnimation.SHOW : ForwardButtonAnimation.HIDE);
+    }
+
+    @Override
+    public void refresh() {
+        super.refresh();
+        forwardButton.setImageDrawable(getResources().getDrawable(R.drawable.ic_menu_forward));
+        backButton.setImageDrawable(getResources().getDrawable(R.drawable.ic_menu_back));
+    }
+
+    @Override
+    public void setNextFocusDownId(int nextId) {
+        super.setNextFocusDownId(nextId);
+        backButton.setNextFocusDownId(nextId);
+        forwardButton.setNextFocusDownId(nextId);
+    }
+
+    @Override
+    public void setPrivateMode(final boolean isPrivate) {
+        super.setPrivateMode(isPrivate);
+
+        if (SkinConfig.isAustralis()) {
+            // If we had backgroundTintList, we could remove the colorFilter
+            // code in favor of setPrivateMode (bug 1197432).
+            final PorterDuffColorFilter colorFilter =
+                    isPrivate ? privateBrowsingTabletMenuItemColorFilter : null;
+            setTabsCounterPrivateMode(isPrivate, colorFilter);
+        }
+
+        backButton.setPrivateMode(isPrivate);
+        forwardButton.setPrivateMode(isPrivate);
+
+        // bug 1375351: menuButton is a ThemedImageButton in Photon flavor
+        if (SkinConfig.isPhoton()) {
+            ((ThemedImageButton)menuButton).setPrivateMode(isPrivate);
+        } else {
+            ((ThemedFrameLayout)menuButton).setPrivateMode(isPrivate);
+            // menuIcon only exists in Australis flavor
+            menuIcon.setPrivateMode(isPrivate);
+        }
+
+        for (int i = 0; i < actionItemBar.getChildCount(); ++i) {
+            final MenuItemActionBar child = (MenuItemActionBar) actionItemBar.getChildAt(i);
+            child.setPrivateMode(isPrivate);
+        }
+    }
+
+    private void setTabsCounterPrivateMode(final boolean isPrivate, final PorterDuffColorFilter colorFilter) {
+        // The TabsCounter is a TextSwitcher which cycles two views
+        // to provide animations, hence looping over these two children.
+        for (int i = 0; i < 2; ++i) {
+            final ThemedTextView view = (ThemedTextView) tabsCounter.getChildAt(i);
+            view.setPrivateMode(isPrivate);
+            view.getBackground().mutate().setColorFilter(colorFilter);
+        }
+
+        // To prevent animation of the background,
+        // it is set to a different Drawable.
+        tabsCounter.getBackground().mutate().setColorFilter(colorFilter);
+    }
+
+    @Override
+    public View getDoorHangerAnchor() {
+        return backButton;
+    }
+
+    protected boolean canDoBack(final Tab tab) {
+        return (tab.canDoBack() && !isEditing());
+    }
+
+    protected boolean canDoForward(final Tab tab) {
+        return (tab.canDoForward() && !isEditing());
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/app/src/photon/java/org/mozilla/gecko/toolbar/ForwardButton.java
@@ -0,0 +1,31 @@
+/* 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 android.content.Context;
+import android.support.v4.view.ViewCompat;
+import android.util.AttributeSet;
+
+public class ForwardButton extends NavButton {
+    public ForwardButton(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
+        super.onSizeChanged(width, height, oldWidth, oldHeight);
+
+        boolean isLayoutRtl = ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL;
+        mBorderPath.reset();
+        final float startX;
+        if (isLayoutRtl) {
+            startX = 0 + mBorderWidth;
+        } else {
+            startX = width - mBorderWidth;
+        }
+        mBorderPath.moveTo(startX, 0);
+        mBorderPath.lineTo(startX, height);
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/app/src/photon/java/org/mozilla/gecko/toolbar/ToolbarDisplayLayout.java
@@ -0,0 +1,524 @@
+/* -*- 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 java.util.Arrays;
+import java.util.EnumSet;
+import java.util.List;
+
+import android.support.v4.content.ContextCompat;
+import org.mozilla.gecko.AboutPages;
+import org.mozilla.gecko.BrowserApp;
+import org.mozilla.gecko.R;
+import org.mozilla.gecko.reader.ReaderModeUtils;
+import org.mozilla.gecko.SiteIdentity;
+import org.mozilla.gecko.Tab;
+import org.mozilla.gecko.Tabs;
+import org.mozilla.gecko.animation.PropertyAnimator;
+import org.mozilla.gecko.animation.ViewHelper;
+import org.mozilla.gecko.skin.SkinConfig;
+import org.mozilla.gecko.toolbar.BrowserToolbarTabletBase.ForwardButtonAnimation;
+import org.mozilla.gecko.Experiments;
+import org.mozilla.gecko.util.HardwareUtils;
+import org.mozilla.gecko.util.StringUtils;
+import org.mozilla.gecko.util.ViewUtil;
+import org.mozilla.gecko.widget.themed.ThemedImageButton;
+import org.mozilla.gecko.widget.themed.ThemedLinearLayout;
+import org.mozilla.gecko.widget.themed.ThemedTextView;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.v4.view.ViewCompat;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
+import android.text.style.ForegroundColorSpan;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageButton;
+
+import org.mozilla.gecko.switchboard.SwitchBoard;
+
+/**
+* {@code ToolbarDisplayLayout} is the UI for when the toolbar is in
+* display state. It's used to display the state of the currently selected
+* tab. It should always be updated through a single entry point
+* (updateFromTab) and should never track any tab events or gecko messages
+* on its own to keep it as dumb as possible.
+*
+* The UI has two possible modes: progress and display which are triggered
+* when UpdateFlags.PROGRESS is used depending on the current tab state.
+* The progress mode is triggered when the tab is loading a page. Display mode
+* is used otherwise.
+*
+* {@code ToolbarDisplayLayout} is meant to be owned by {@code BrowserToolbar}
+* which is the main event bus for the toolbar subsystem.
+*/
+public class ToolbarDisplayLayout extends ThemedLinearLayout {
+
+    private static final String LOGTAG = "GeckoToolbarDisplayLayout";
+    private boolean mTrackingProtectionEnabled;
+
+    // To be used with updateFromTab() to allow the caller
+    // to give enough context for the requested state change.
+    enum UpdateFlags {
+        TITLE,
+        FAVICON,
+        PROGRESS,
+        SITE_IDENTITY,
+        PRIVATE_MODE,
+
+        // Disable any animation that might be
+        // triggered from this state change. Mostly
+        // used on tab switches, see BrowserToolbar.
+        DISABLE_ANIMATIONS
+    }
+
+    private enum UIMode {
+        PROGRESS,
+        DISPLAY
+    }
+
+    interface OnStopListener {
+        Tab onStop();
+    }
+
+    interface OnTitleChangeListener {
+        void onTitleChange(CharSequence title);
+    }
+
+    private final BrowserApp mActivity;
+
+    private UIMode mUiMode;
+
+    private boolean mIsAttached;
+
+    private final ThemedTextView mTitle;
+    private final int mTitlePadding;
+    private ToolbarPrefs mPrefs;
+    private OnTitleChangeListener mTitleChangeListener;
+
+    private final ThemedImageButton mSiteSecurity;
+    private final ImageButton mStop;
+    private OnStopListener mStopListener;
+
+    private final PageActionLayout mPageActionLayout;
+
+    private final SiteIdentityPopup mSiteIdentityPopup;
+    private int mSecurityImageLevel;
+
+    // Security level constants, which map to the icons / levels defined in:
+    // http://dxr.mozilla.org/mozilla-central/source/mobile/android/base/java/org/mozilla/gecko/resources/drawable/site_security_level.xml
+    // Default level (unverified pages) - globe icon:
+    private static final int LEVEL_DEFAULT_GLOBE = 0;
+    // Levels for displaying Mixed Content state icons.
+    private static final int LEVEL_WARNING_MINOR = 3;
+    private static final int LEVEL_LOCK_DISABLED = 4;
+    // Levels for displaying Tracking Protection state icons.
+    private static final int LEVEL_SHIELD_ENABLED = 5;
+    private static final int LEVEL_SHIELD_DISABLED = 6;
+    // Icon used for about:home
+    private static final int LEVEL_SEARCH_ICON = 999;
+
+    private final ForegroundColorSpan mUrlColorSpan;
+    private final ForegroundColorSpan mPrivateUrlColorSpan;
+    private final ForegroundColorSpan mBlockedColorSpan;
+    private final ForegroundColorSpan mPrivateBlockedColorSpan;
+    private final ForegroundColorSpan mDomainColorSpan;
+    private final ForegroundColorSpan mPrivateDomainColorSpan;
+    private final ForegroundColorSpan mCertificateOwnerColorSpan;
+    private final ForegroundColorSpan mPrivateCertificateOwnerColorSpan;
+
+    public ToolbarDisplayLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setOrientation(HORIZONTAL);
+
+        mActivity = (BrowserApp) context;
+
+        LayoutInflater.from(context).inflate(R.layout.toolbar_display_layout, this);
+
+        mTitle = (ThemedTextView) findViewById(R.id.url_bar_title);
+        mTitlePadding = mTitle.getPaddingRight();
+
+        mUrlColorSpan = new ForegroundColorSpan(ContextCompat.getColor(context, R.color.url_bar_urltext));
+        mPrivateUrlColorSpan = new ForegroundColorSpan(ContextCompat.getColor(context, R.color.url_bar_urltext_private));
+        mBlockedColorSpan = new ForegroundColorSpan(ContextCompat.getColor(context, R.color.url_bar_blockedtext));
+        mPrivateBlockedColorSpan = new ForegroundColorSpan(ContextCompat.getColor(context, R.color.url_bar_blockedtext_private));
+        mDomainColorSpan = new ForegroundColorSpan(ContextCompat.getColor(context, R.color.url_bar_domaintext));
+        mPrivateDomainColorSpan = new ForegroundColorSpan(ContextCompat.getColor(context, R.color.url_bar_domaintext_private));
+        mCertificateOwnerColorSpan = new ForegroundColorSpan(ContextCompat.getColor(context, R.color.url_bar_certificate_owner));
+        mPrivateCertificateOwnerColorSpan = new ForegroundColorSpan(ContextCompat.getColor(context, R.color.url_bar_certificate_owner_private));
+
+        mSiteSecurity = (ThemedImageButton) findViewById(R.id.site_security);
+
+        mSiteIdentityPopup = new SiteIdentityPopup(mActivity);
+        mSiteIdentityPopup.setAnchor(this);
+        mSiteIdentityPopup.setOnVisibilityChangeListener(mActivity);
+
+        mStop = (ImageButton) findViewById(R.id.stop);
+        mPageActionLayout = (PageActionLayout) findViewById(R.id.page_action_layout);
+    }
+
+    @Override
+    public void setPrivateMode(boolean isPrivate) {
+        super.setPrivateMode(isPrivate);
+        mSiteSecurity.setPrivateMode(isPrivate);
+
+        // Bug 1375351 should change class type to ThemedImageButton to avoid casting
+        if (SkinConfig.isPhoton()) {
+            ((ThemedImageButton)mStop).setPrivateMode(isPrivate);
+        }
+
+        mPageActionLayout.setPrivateMode(isPrivate);
+    }
+
+
+    @Override
+    public void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
+        mIsAttached = true;
+
+        final Tab selectedTab = Tabs.getInstance().getSelectedTab();
+        if (selectedTab != null) {
+            // A tab was selected before we became ready; update to that tab now.
+            updateFromTab(selectedTab, EnumSet.allOf(UpdateFlags.class));
+        }
+
+        mSiteIdentityPopup.registerListeners();
+
+        mSiteSecurity.setOnClickListener(new Button.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                mSiteIdentityPopup.show();
+            }
+        });
+
+        mStop.setOnClickListener(new Button.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (mStopListener != null) {
+                    // Force toolbar to switch to Display mode
+                    // immediately based on the stopped tab.
+                    final Tab tab = mStopListener.onStop();
+                    if (tab != null) {
+                        updateUiMode(UIMode.DISPLAY);
+                    }
+                }
+            }
+        });
+    }
+
+    @Override
+    public void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mIsAttached = false;
+        mSiteIdentityPopup.unregisterListeners();
+    }
+
+    @Override
+    public void setNextFocusDownId(int nextId) {
+        mStop.setNextFocusDownId(nextId);
+        mSiteSecurity.setNextFocusDownId(nextId);
+        mPageActionLayout.setNextFocusDownId(nextId);
+    }
+
+    void setToolbarPrefs(final ToolbarPrefs prefs) {
+        mPrefs = prefs;
+    }
+
+    void updateFromTab(@NonNull Tab tab, EnumSet<UpdateFlags> flags) {
+        // Several parts of ToolbarDisplayLayout's state depends
+        // on the views being attached to the view tree.
+        if (!mIsAttached) {
+            return;
+        }
+
+        if (flags.contains(UpdateFlags.TITLE)) {
+            updateTitle(tab);
+        }
+
+        if (flags.contains(UpdateFlags.SITE_IDENTITY)) {
+            updateSiteIdentity(tab);
+        }
+
+        if (flags.contains(UpdateFlags.PROGRESS)) {
+            updateProgress(tab);
+        }
+
+        if (flags.contains(UpdateFlags.PRIVATE_MODE)) {
+            mTitle.setPrivateMode(tab.isPrivate());
+        }
+    }
+
+    void setTitle(CharSequence title) {
+        mTitle.setText(title);
+
+        if (TextUtils.isEmpty(title)) {
+            //  Reset TextDirection to Locale in order to reveal text hint in correct direction
+            ViewUtil.setTextDirection(mTitle, TEXT_DIRECTION_LOCALE);
+        } else {
+            //  Otherwise, fall back to default first strong strategy
+            ViewUtil.setTextDirection(mTitle, TEXT_DIRECTION_FIRST_STRONG);
+        }
+
+        if (mTitleChangeListener != null) {
+            mTitleChangeListener.onTitleChange(title);
+        }
+    }
+
+    private void updateTitle(@NonNull Tab tab) {
+        // Keep the title unchanged if there's no selected tab,
+        // or if the tab is entering reader mode.
+        if (tab.isEnteringReaderMode()) {
+            return;
+        }
+
+        final String url = tab.getURL();
+
+        // Setting a null title will ensure we just see the
+        // "Enter Search or Address" placeholder text.
+        if (AboutPages.isTitlelessAboutPage(url)) {
+            setTitle(null);
+            setContentDescription(null);
+            return;
+        }
+
+        // Show the about:blocked page title in red, regardless of prefs
+        if (tab.getErrorType() == Tab.ErrorType.BLOCKED) {
+            final String title = tab.getDisplayTitle();
+
+            final SpannableStringBuilder builder = new SpannableStringBuilder(title);
+            final ForegroundColorSpan fgColorSpan = tab.isPrivate()
+                    ? mPrivateBlockedColorSpan
+                    : mBlockedColorSpan;
+            builder.setSpan(fgColorSpan, 0, title.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
+
+            setTitle(builder);
+            setContentDescription(null);
+            return;
+        }
+
+        final String baseDomain = tab.getBaseDomain();
+
+        String strippedURL = stripAboutReaderURL(url);
+
+        final boolean isHttpOrHttps = StringUtils.isHttpOrHttps(strippedURL);
+
+        if (mPrefs.shouldTrimUrls()) {
+            strippedURL = StringUtils.stripCommonSubdomains(StringUtils.stripScheme(strippedURL));
+        }
+
+        // The URL bar does not support RTL currently (See bug 928688 and meta bug 702845).
+        // Displaying a URL using RTL (or mixed) characters can lead to an undesired reordering
+        // of elements of the URL. That's why we are forcing the URL to use LTR (bug 1284372).
+        strippedURL = StringUtils.forceLTR(strippedURL);
+
+        // This value is not visible to screen readers but we rely on it when running UI tests. Screen
+        // readers will instead focus BrowserToolbar and read the "base domain" from there. UI tests
+        // will read the content description to obtain the full URL for performing assertions.
+        setContentDescription(strippedURL);
+
+        final SiteIdentity siteIdentity = tab.getSiteIdentity();
+        if (siteIdentity.hasOwner() && SwitchBoard.isInExperiment(mActivity, Experiments.URLBAR_SHOW_EV_CERT_OWNER)) {
+            // Show Owner of EV certificate as title
+            updateTitleFromSiteIdentity(siteIdentity, tab.isPrivate());
+        } else if (isHttpOrHttps && !HardwareUtils.isTablet() && !TextUtils.isEmpty(baseDomain)
+                && SwitchBoard.isInExperiment(mActivity, Experiments.URLBAR_SHOW_ORIGIN_ONLY)) {
+            // Show just the base domain as title
+            setTitle(baseDomain);
+        } else {
+            // Display full URL with base domain highlighted as title
+            updateAndColorTitleFromFullURL(strippedURL, baseDomain, tab.isPrivate());
+        }
+    }
+
+    private void updateTitleFromSiteIdentity(SiteIdentity siteIdentity, boolean isPrivate) {
+        final String title;
+
+        if (siteIdentity.hasCountry()) {
+            title = String.format("%s (%s)", siteIdentity.getOwner(), siteIdentity.getCountry());
+        } else {
+            title = siteIdentity.getOwner();
+        }
+
+        final SpannableString spannable = new SpannableString(title);
+        final ForegroundColorSpan colorSpan = isPrivate
+                ? mPrivateCertificateOwnerColorSpan
+                : mCertificateOwnerColorSpan;
+        spannable.setSpan(colorSpan, 0, title.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+        setTitle(spannable);
+    }
+
+    private void updateAndColorTitleFromFullURL(String url, String baseDomain, boolean isPrivate) {
+        if (TextUtils.isEmpty(baseDomain)) {
+            setTitle(url);
+            return;
+        }
+
+        int index = url.indexOf(baseDomain);
+        if (index == -1) {
+            setTitle(url);
+            return;
+        }
+
+        final SpannableStringBuilder builder = new SpannableStringBuilder(url);
+
+        builder.setSpan(isPrivate ? mPrivateUrlColorSpan : mUrlColorSpan, 0, url.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
+        builder.setSpan(isPrivate ? mPrivateDomainColorSpan : mDomainColorSpan,
+                index, index + baseDomain.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
+
+        setTitle(builder);
+    }
+
+    private String stripAboutReaderURL(final String url) {
+        if (!AboutPages.isAboutReader(url)) {
+            return url;
+        }
+
+        return ReaderModeUtils.stripAboutReaderUrl(url);
+    }
+
+    private void updateSiteIdentity(@NonNull Tab tab) {
+        final SiteIdentity siteIdentity = tab.getSiteIdentity();
+        final SecurityModeUtil.IconType type = SecurityModeUtil.resolve(siteIdentity, tab.getURL());
+        final int imageLevel = SecurityModeUtil.getImageLevel(type);
+
+        mSiteIdentityPopup.setSiteIdentity(siteIdentity);
+        mTrackingProtectionEnabled = SecurityModeUtil.isTrackingProtectionEnabled(siteIdentity);
+
+        if (mSecurityImageLevel != imageLevel) {
+            mSecurityImageLevel = imageLevel;
+            mSiteSecurity.setImageLevel(mSecurityImageLevel);
+            updatePageActions();
+        }
+    }
+
+    private void updateProgress(@NonNull Tab tab) {
+        final boolean shouldShowThrobber = tab.getState() == Tab.STATE_LOADING;
+
+        updateUiMode(shouldShowThrobber ? UIMode.PROGRESS : UIMode.DISPLAY);
+
+        if (Tab.STATE_SUCCESS == tab.getState() && mTrackingProtectionEnabled) {
+            mActivity.showTrackingProtectionPromptIfApplicable();
+        }
+    }
+
+    private void updateUiMode(UIMode uiMode) {
+        if (mUiMode == uiMode) {
+            return;
+        }
+
+        mUiMode = uiMode;
+        updatePageActions();
+    }
+
+    private void updatePageActions() {
+        final boolean isShowingProgress = (mUiMode == UIMode.PROGRESS);
+
+        mStop.setVisibility(isShowingProgress ? View.VISIBLE : View.GONE);
+        mPageActionLayout.setVisibility(!isShowingProgress ? View.VISIBLE : View.GONE);
+
+        // We want title to fill the whole space available for it when there are icons
+        // being shown on the right side of the toolbar as the icons already have some
+        // padding in them. This is just to avoid wasting space when icons are shown.
+        mTitle.setPadding(0, 0, (!isShowingProgress ? mTitlePadding : 0), 0);
+    }
+
+    List<? extends View> getFocusOrder() {
+        return Arrays.asList(mSiteSecurity, mPageActionLayout, mStop);
+    }
+
+    void setOnStopListener(OnStopListener listener) {
+        mStopListener = listener;
+    }
+
+    void setOnTitleChangeListener(OnTitleChangeListener listener) {
+        mTitleChangeListener = listener;
+    }
+
+    /**
+     * Update the Site Identity popup anchor.
+     *
+     * Tablet UI has a tablet-specific doorhanger anchor, so update it after all the views
+     * are inflated.
+     * @param view View to use as the anchor for the Site Identity popup.
+     */
+    void updateSiteIdentityAnchor(View view) {
+        mSiteIdentityPopup.setAnchor(view);
+    }
+
+    private boolean isLayoutRtl() {
+        return ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL;
+    }
+
+    void prepareForwardAnimation(PropertyAnimator anim, ForwardButtonAnimation animation, int width) {
+        if (animation == ForwardButtonAnimation.HIDE) {
+            // We animate these items individually, rather than this entire view,
+            // so that we don't animate certain views, e.g. the stop button.
+            anim.attach(mTitle,
+                        PropertyAnimator.Property.TRANSLATION_X,
+                        isLayoutRtl() ? width : 0);
+            anim.attach(mSiteSecurity,
+                        PropertyAnimator.Property.TRANSLATION_X,
+                        isLayoutRtl() ? width : 0);
+
+            // We're hiding the forward button. We're going to reset the margin before
+            // the animation starts, so we shift these items to the right so that they don't
+            // appear to move initially.
+            ViewHelper.setTranslationX(mTitle, width);
+            ViewHelper.setTranslationX(mSiteSecurity, width);
+        } else {
+            anim.attach(mTitle,
+                        PropertyAnimator.Property.TRANSLATION_X,
+                        isLayoutRtl() ? 0 : width);
+            anim.attach(mSiteSecurity,
+                        PropertyAnimator.Property.TRANSLATION_X,
+                        isLayoutRtl() ? 0 : width);
+        }
+    }
+
+    void finishForwardAnimation() {
+        ViewHelper.setTranslationX(mTitle, 0);
+        ViewHelper.setTranslationX(mSiteSecurity, 0);
+    }
+
+    void prepareStartEditingAnimation() {
+        // Hide page actions/stop buttons immediately
+        ViewHelper.setAlpha(mPageActionLayout, 0);
+        ViewHelper.setAlpha(mStop, 0);
+    }
+
+    void prepareStopEditingAnimation(PropertyAnimator anim) {
+        // Fade toolbar buttons (page actions, stop) after the entry
+        // is shrunk back to its original size.
+        anim.attach(mPageActionLayout,
+                    PropertyAnimator.Property.ALPHA,
+                    1);
+
+        anim.attach(mStop,
+                    PropertyAnimator.Property.ALPHA,
+                    1);
+    }
+
+    boolean dismissSiteIdentityPopup() {
+        if (mSiteIdentityPopup != null && mSiteIdentityPopup.isShowing()) {
+            mSiteIdentityPopup.dismiss();
+            return true;
+        }
+
+        return false;
+    }
+
+    void destroy() {
+        mSiteIdentityPopup.destroy();
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/app/src/photon/res/color/action_bar_menu_item_colors.xml
@@ -0,0 +1,22 @@
+<?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/. -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+          xmlns:gecko="http://schemas.android.com/apk/res-auto">
+
+    <item gecko:state_private="true"
+          android:state_enabled="false"
+          android:color="@color/photon_icon_private"/>
+
+    <item gecko:state_private="true"
+          android:state_enabled="true"
+          android:color="@color/photon_icon_private"/>
+
+    <item android:state_enabled="false"
+          android:color="@color/photon_icon"/>
+
+    <item android:color="@color/photon_icon"/>
+
+</selector>
new file mode 100644
--- /dev/null
+++ b/mobile/android/app/src/photon/res/color/tab_new_tab_strip_colors.xml
@@ -0,0 +1,14 @@
+<?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/. -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+          xmlns:gecko="http://schemas.android.com/apk/res-auto">
+
+    <item gecko:state_light="true"
+          android:color="@color/photon_icon"/>
+
+    <item android:color="@color/photon_icon_private"/>
+
+</selector>
new file mode 100644
--- /dev/null
+++ b/mobile/android/app/src/photon/res/drawable-large-v11/url_bar_nav_button.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/. -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+          xmlns:gecko="http://schemas.android.com/apk/res-auto">
+
+    <!-- private pressed state -->
+    <item gecko:state_private="true"
+          android:state_pressed="true"
+          android:drawable="@color/text_and_tabs_tray_grey"/>
+
+    <!-- focused state -->
+    <item gecko:state_private="true"
+          android:state_focused="true"
+          android:state_pressed="false"
+          android:drawable="@color/placeholder_active_grey"/>
+
+    <!-- pressed state -->
+    <item android:state_pressed="true"
+          android:drawable="@color/toolbar_grey_pressed"/>
+
+    <!-- focused state -->
+    <item android:state_focused="true"
+          android:state_pressed="false"
+          android:drawable="@color/tablet_highlight_focused"/>
+
+    <!-- private browsing mode -->
+    <item gecko:state_private="true"
+          android:drawable="@color/tabs_tray_grey_pressed"/>
+
+    <!-- normal mode -->
+    <item android:drawable="@color/toolbar_grey"/>
+
+</selector>