diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java
index 02490f2c804b..6c941be1e843 100644
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -1322,7 +1322,7 @@ public class BrowserApp extends GeckoApp
if (mMainLayoutAnimator != null)
mMainLayoutAnimator.stop();
- boolean isSideBar = (HardwareUtils.isTablet() && getOrientation() == Configuration.ORIENTATION_LANDSCAPE);
+ boolean isSideBar = !NewTabletUI.isEnabled(this) && (HardwareUtils.isTablet() && getOrientation() == Configuration.ORIENTATION_LANDSCAPE);
final int sidebarWidth = getResources().getDimensionPixelSize(R.dimen.tabs_sidebar_width);
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) mTabsPanel.getLayoutParams();
diff --git a/mobile/android/base/moz.build b/mobile/android/base/moz.build
index 1ce71e888a37..04018c9e829a 100644
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -395,6 +395,7 @@ gbjar.sources += [
'tabs/RemoteTabsSetupPanel.java',
'tabs/RemoteTabsVerificationPanel.java',
'tabs/TabCurve.java',
+ 'tabs/TabsGridLayout.java',
'tabs/TabsLayoutAdapter.java',
'tabs/TabsLayoutItemView.java',
'tabs/TabsListLayout.java',
diff --git a/mobile/android/base/resources/values-v11/themes.xml b/mobile/android/base/resources/values-v11/themes.xml
index a4c9a83ee37c..e15f92162c8b 100644
--- a/mobile/android/base/resources/values-v11/themes.xml
+++ b/mobile/android/base/resources/values-v11/themes.xml
@@ -46,6 +46,7 @@
- @style/Widget.MenuItemActionView
- @style/Widget.MenuItemDefault
- @style/Widget.MenuItemSecondaryActionBar
+ - @style/Widget.TabsGridLayout
diff --git a/mobile/android/base/resources/values/attrs.xml b/mobile/android/base/resources/values/attrs.xml
index a9f1212eadea..9783bcd7af74 100644
--- a/mobile/android/base/resources/values/attrs.xml
+++ b/mobile/android/base/resources/values/attrs.xml
@@ -38,6 +38,9 @@
+
+
+
diff --git a/mobile/android/base/resources/values/dimens.xml b/mobile/android/base/resources/values/dimens.xml
index b340a6de3680..50ed658e8cd0 100644
--- a/mobile/android/base/resources/values/dimens.xml
+++ b/mobile/android/base/resources/values/dimens.xml
@@ -105,6 +105,9 @@
150dp
+
+ 200dp
+
95dp
diff --git a/mobile/android/base/resources/values/styles.xml b/mobile/android/base/resources/values/styles.xml
index ca3929f88457..f2ce81cd812e 100644
--- a/mobile/android/base/resources/values/styles.xml
+++ b/mobile/android/base/resources/values/styles.xml
@@ -179,6 +179,18 @@
- true
+
+
diff --git a/mobile/android/base/resources/values/themes.xml b/mobile/android/base/resources/values/themes.xml
index c5b8df8a32bb..3e5f0ecf6bdf 100644
--- a/mobile/android/base/resources/values/themes.xml
+++ b/mobile/android/base/resources/values/themes.xml
@@ -89,6 +89,7 @@
- @android:color/white
- @style/Widget.BookmarksListView
- @style/FloatingHintEditText
+ - @style/Widget.TabsGridLayout
- @style/Widget.GeckoMenuListView
- @style/Widget.HomeListView
- @style/Widget.MenuItemActionBar
diff --git a/mobile/android/base/tabs/TabsGridLayout.java b/mobile/android/base/tabs/TabsGridLayout.java
new file mode 100644
index 000000000000..1a6e32a09c54
--- /dev/null
+++ b/mobile/android/base/tabs/TabsGridLayout.java
@@ -0,0 +1,239 @@
+/* -*- 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.tabs;
+
+import java.util.ArrayList;
+
+import org.mozilla.gecko.animation.ViewHelper;
+import org.mozilla.gecko.GeckoAppShell;
+import org.mozilla.gecko.GeckoEvent;
+import org.mozilla.gecko.R;
+import org.mozilla.gecko.Tab;
+import org.mozilla.gecko.tabs.TabsPanel.TabsLayout;
+import org.mozilla.gecko.Tabs;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.GridView;
+import android.view.ViewGroup;
+import android.widget.Button;
+
+/**
+ * A tabs layout implementation for the tablet redesign (bug 1014156).
+ * Expected to replace TabsListLayout once complete.
+ */
+
+class TabsGridLayout extends GridView
+ implements TabsLayout,
+ Tabs.OnTabsChangedListener {
+ private static final String LOGTAG = "Gecko" + TabsGridLayout.class.getSimpleName();
+
+ private Context mContext;
+ private TabsPanel mTabsPanel;
+
+ final private boolean mIsPrivate;
+
+ private TabsLayoutAdapter mTabsAdapter;
+
+ public TabsGridLayout(Context context, AttributeSet attrs) {
+ super(context, attrs, R.attr.tabGridLayoutViewStyle);
+ mContext = context;
+
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabsTray);
+ mIsPrivate = (a.getInt(R.styleable.TabsTray_tabs, 0x0) == 1);
+ a.recycle();
+
+ mTabsAdapter = new TabsGridLayoutAdapter(mContext);
+ setAdapter(mTabsAdapter);
+
+ setRecyclerListener(new RecyclerListener() {
+ @Override
+ public void onMovedToScrapHeap(View view) {
+ TabsLayoutItemView item = (TabsLayoutItemView) view.getTag();
+ item.thumbnail.setImageDrawable(null);
+ item.close.setVisibility(View.VISIBLE);
+ }
+ });
+ }
+
+ private class TabsGridLayoutAdapter extends TabsLayoutAdapter {
+
+ final private Button.OnClickListener mCloseClickListener;
+ final private View.OnClickListener mSelectClickListener;
+
+ public TabsGridLayoutAdapter (Context context) {
+ super(context);
+
+ mCloseClickListener = new Button.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ TabsLayoutItemView itemView = (TabsLayoutItemView) v.getTag();
+ Tab tab = Tabs.getInstance().getTab(itemView.id);
+ Tabs.getInstance().closeTab(tab);
+ }
+ };
+
+ mSelectClickListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ TabsLayoutItemView tab = (TabsLayoutItemView) v.getTag();
+ Tabs.getInstance().selectTab(tab.id);
+ TabsGridLayout.this.autoHidePanel();
+ }
+ };
+ }
+
+ @Override
+ public View newView(int position, ViewGroup parent) {
+ View view = super.newView(position, parent);
+
+ // This is nasty and once we change TabsLayoutItemView to an actual view
+ // we can get rid of it.
+ TabsLayoutItemView item = (TabsLayoutItemView) view.getTag();
+ item.close.setOnClickListener(mCloseClickListener);
+
+ return view;
+ }
+
+ @Override
+ public void bindView(View view, Tab tab) {
+ super.bindView(view, tab);
+
+ view.setOnClickListener(mSelectClickListener);
+
+ // If we're recycling this view, there's a chance it was transformed during
+ // the close animation. Remove any of those properties.
+ TabsGridLayout.this.resetTransforms(view);
+ }
+ }
+
+ @Override
+ public void setTabsPanel(TabsPanel panel) {
+ mTabsPanel = panel;
+ }
+
+ @Override
+ public void show() {
+ setVisibility(View.VISIBLE);
+ Tabs.getInstance().refreshThumbnails();
+ Tabs.registerOnTabsChangedListener(this);
+ refreshTabsData();
+ }
+
+ @Override
+ public void hide() {
+ setVisibility(View.GONE);
+ Tabs.unregisterOnTabsChangedListener(this);
+ GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Tab:Screenshot:Cancel",""));
+ mTabsAdapter.clear();
+ }
+
+ @Override
+ public boolean shouldExpand() {
+ return true;
+ }
+
+ private void autoHidePanel() {
+ mTabsPanel.autoHidePanel();
+ }
+
+ @Override
+ public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) {
+ switch (msg) {
+ case ADDED:
+ // Refresh the list to make sure the new tab is added in the right position.
+ refreshTabsData();
+ break;
+
+ case CLOSED:
+ if (tab.isPrivate() == mIsPrivate && mTabsAdapter.getCount() > 0) {
+ if (mTabsAdapter.removeTab(tab)) {
+ int selected = mTabsAdapter.getPositionForTab(Tabs.getInstance().getSelectedTab());
+ updateSelectedStyle(selected);
+ }
+ }
+ break;
+
+ case SELECTED:
+ // Update the selected position, then fall through...
+ updateSelectedPosition();
+ case UNSELECTED:
+ // We just need to update the style for the unselected tab...
+ case THUMBNAIL:
+ case TITLE:
+ case RECORDING_CHANGE:
+ View view = getChildAt(mTabsAdapter.getPositionForTab(tab) - getFirstVisiblePosition());
+ if (view == null)
+ return;
+
+ TabsLayoutItemView item = (TabsLayoutItemView) view.getTag();
+ item.assignValues(tab);
+ break;
+ }
+ }
+
+ // Updates the selected position in the list so that it will be scrolled to the right place.
+ private void updateSelectedPosition() {
+ int selected = mTabsAdapter.getPositionForTab(Tabs.getInstance().getSelectedTab());
+ updateSelectedStyle(selected);
+
+ if (selected != -1) {
+ setSelection(selected);
+ }
+ }
+
+ /**
+ * Updates the selected/unselected style for the tabs.
+ *
+ * @param selected position of the selected tab
+ */
+ private void updateSelectedStyle(int selected) {
+ for (int i = 0; i < mTabsAdapter.getCount(); i++) {
+ setItemChecked(i, (i == selected));
+ }
+ }
+
+ private void refreshTabsData() {
+ // Store a different copy of the tabs, so that we don't have to worry about
+ // accidentally updating it on the wrong thread.
+ ArrayList tabData = new ArrayList<>();
+
+ Iterable allTabs = Tabs.getInstance().getTabsInOrder();
+ for (Tab tab : allTabs) {
+ if (tab.isPrivate() == mIsPrivate)
+ tabData.add(tab);
+ }
+
+ mTabsAdapter.setTabs(tabData);
+ updateSelectedPosition();
+ }
+
+ private void resetTransforms(View view) {
+ ViewHelper.setAlpha(view, 1);
+ ViewHelper.setTranslationX(view, 0);
+ }
+
+ @Override
+ public void closeAll() {
+
+ autoHidePanel();
+
+ if (getChildCount() == 0) {
+ return;
+ }
+
+ final Iterable tabs = Tabs.getInstance().getTabsInOrder();
+ for (Tab tab : tabs) {
+ // In the normal panel we want to close all tabs (both private and normal),
+ // but in the private panel we only want to close private tabs.
+ if (!mIsPrivate || tab.isPrivate()) {
+ Tabs.getInstance().closeTab(tab, false);
+ }
+ }
+ }
+}