Merge "Ensure NotificationCompat.MediaStyle methods return MediaStyle" into lmp-mr1-ub-dev
diff --git a/design/api/current.txt b/design/api/current.txt
index 217ba99..9dc6e20 100644
--- a/design/api/current.txt
+++ b/design/api/current.txt
@@ -170,7 +170,9 @@
     ctor public FloatingActionButton(android.content.Context);
     ctor public FloatingActionButton(android.content.Context, android.util.AttributeSet);
     ctor public FloatingActionButton(android.content.Context, android.util.AttributeSet, int);
+    method public void hide();
     method public void setRippleColor(int);
+    method public void show();
   }
 
   public static class FloatingActionButton.Behavior extends android.support.design.widget.CoordinatorLayout.Behavior {
diff --git a/design/base/android/support/design/widget/FloatingActionButtonImpl.java b/design/base/android/support/design/widget/FloatingActionButtonImpl.java
index 6c154d4..7fcb147 100644
--- a/design/base/android/support/design/widget/FloatingActionButtonImpl.java
+++ b/design/base/android/support/design/widget/FloatingActionButtonImpl.java
@@ -26,6 +26,8 @@
 
 abstract class FloatingActionButtonImpl {
 
+    static final int SHOW_HIDE_ANIM_DURATION = 200;
+
     static final int[] PRESSED_ENABLED_STATE_SET = {android.R.attr.state_pressed,
             android.R.attr.state_enabled};
     static final int[] FOCUSED_ENABLED_STATE_SET = {android.R.attr.state_focused,
@@ -57,6 +59,10 @@
 
     abstract void jumpDrawableToCurrentState();
 
+    abstract void hide();
+
+    abstract void show();
+
     Drawable createBorderDrawable(int borderWidth, ColorStateList backgroundTint) {
         final Resources resources = mView.getResources();
         CircularBorderDrawable borderDrawable = newCircularDrawable();
diff --git a/design/eclair-mr1/android/support/design/widget/FloatingActionButtonEclairMr1.java b/design/eclair-mr1/android/support/design/widget/FloatingActionButtonEclairMr1.java
index 36ddd89..1aa0b683 100644
--- a/design/eclair-mr1/android/support/design/widget/FloatingActionButtonEclairMr1.java
+++ b/design/eclair-mr1/android/support/design/widget/FloatingActionButtonEclairMr1.java
@@ -23,6 +23,7 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
 import android.graphics.drawable.LayerDrawable;
+import android.support.design.R;
 import android.support.v4.graphics.drawable.DrawableCompat;
 import android.view.View;
 import android.view.animation.Animation;
@@ -42,6 +43,8 @@
 
     ShadowDrawableWrapper mShadowDrawable;
 
+    private boolean mIsHiding;
+
     FloatingActionButtonEclairMr1(View view, ShadowViewDelegate shadowViewDelegate) {
         super(view, shadowViewDelegate);
 
@@ -153,6 +156,41 @@
         mStateListAnimator.jumpToCurrentState();
     }
 
+    @Override
+    void hide() {
+        if (mIsHiding) {
+            // There is currently an hide animation running, return
+            return;
+        }
+
+        Animation anim = android.view.animation.AnimationUtils.loadAnimation(
+                mView.getContext(), R.anim.fab_out);
+        anim.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
+        anim.setDuration(SHOW_HIDE_ANIM_DURATION);
+        anim.setAnimationListener(new AnimationUtils.AnimationListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animation animation) {
+                mIsHiding = true;
+            }
+
+            @Override
+            public void onAnimationEnd(Animation animation) {
+                mIsHiding = false;
+                mView.setVisibility(View.GONE);
+            }
+        });
+        mView.startAnimation(anim);
+    }
+
+    @Override
+    void show() {
+        Animation anim = android.view.animation.AnimationUtils.loadAnimation(
+                mView.getContext(), R.anim.fab_in);
+        anim.setDuration(SHOW_HIDE_ANIM_DURATION);
+        anim.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
+        mView.startAnimation(anim);
+    }
+
     private void updatePadding() {
         Rect rect = new Rect();
         mShadowDrawable.getPadding(rect);
diff --git a/design/honeycomb-mr1/android/support/design/widget/FloatingActionButtonHoneycombMr1.java b/design/honeycomb-mr1/android/support/design/widget/FloatingActionButtonHoneycombMr1.java
new file mode 100644
index 0000000..62da58e
--- /dev/null
+++ b/design/honeycomb-mr1/android/support/design/widget/FloatingActionButtonHoneycombMr1.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.design.widget;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.view.View;
+
+class FloatingActionButtonHoneycombMr1 extends FloatingActionButtonEclairMr1 {
+
+    private boolean mIsHiding;
+
+    FloatingActionButtonHoneycombMr1(View view, ShadowViewDelegate shadowViewDelegate) {
+        super(view, shadowViewDelegate);
+    }
+
+    @Override
+    void hide() {
+        if (mIsHiding) {
+            // A hide animation is in progress, skip the call
+            return;
+        }
+
+        mView.animate()
+                .scaleX(0f)
+                .scaleY(0f)
+                .alpha(0f)
+                .setDuration(SHOW_HIDE_ANIM_DURATION)
+                .setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR)
+                .setListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationStart(Animator animation) {
+                        mIsHiding = true;
+                    }
+
+                    @Override
+                    public void onAnimationCancel(Animator animation) {
+                        mIsHiding = false;
+                    }
+
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        mIsHiding = false;
+                        mView.setVisibility(View.GONE);
+                    }
+                });
+    }
+
+    @Override
+    void show() {
+        mView.animate()
+                .scaleX(1f)
+                .scaleY(1f)
+                .alpha(1f)
+                .setDuration(SHOW_HIDE_ANIM_DURATION)
+                .setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR)
+                .setListener(null);
+    }
+}
diff --git a/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java b/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java
index c21d341..a440427 100644
--- a/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java
+++ b/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java
@@ -33,7 +33,7 @@
 import android.view.animation.Interpolator;
 
 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
-class FloatingActionButtonLollipop extends FloatingActionButtonImpl {
+class FloatingActionButtonLollipop extends FloatingActionButtonHoneycombMr1 {
 
     private Drawable mShapeDrawable;
     private RippleDrawable mRippleDrawable;
diff --git a/design/res/layout-sw600dp/layout_snackbar.xml b/design/res/layout-sw600dp/layout_snackbar.xml
index 6605408..b68395a 100644
--- a/design/res/layout-sw600dp/layout_snackbar.xml
+++ b/design/res/layout-sw600dp/layout_snackbar.xml
@@ -19,5 +19,5 @@
       class="android.support.design.widget.Snackbar$SnackbarLayout"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
-      android:layout_gravity="bottom|center_vertical"
+      android:layout_gravity="bottom|center_horizontal"
       style="@style/Widget.Design.Snackbar" />
\ No newline at end of file
diff --git a/design/res/layout/layout_tab_icon.xml b/design/res/layout/layout_tab_icon.xml
new file mode 100644
index 0000000..6464d1f
--- /dev/null
+++ b/design/res/layout/layout_tab_icon.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+           android:layout_width="wrap_content"
+           android:layout_height="wrap_content"
+           android:layout_gravity="center"/>
\ No newline at end of file
diff --git a/design/res/layout/layout_tab_text.xml b/design/res/layout/layout_tab_text.xml
new file mode 100644
index 0000000..a83bb3d
--- /dev/null
+++ b/design/res/layout/layout_tab_text.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:ellipsize="end"
+          android:gravity="center"
+          android:maxLines="2"/>
\ No newline at end of file
diff --git a/design/src/android/support/design/internal/NavigationMenuPresenter.java b/design/src/android/support/design/internal/NavigationMenuPresenter.java
index f19b78a..6f374d3 100644
--- a/design/src/android/support/design/internal/NavigationMenuPresenter.java
+++ b/design/src/android/support/design/internal/NavigationMenuPresenter.java
@@ -463,7 +463,9 @@
 
         public Bundle createInstanceState() {
             Bundle state = new Bundle();
-            state.putInt(STATE_CHECKED_ITEM, mCheckedItem.getItemId());
+            if (mCheckedItem != null) {
+                state.putInt(STATE_CHECKED_ITEM, mCheckedItem.getItemId());
+            }
             return state;
         }
 
diff --git a/design/src/android/support/design/widget/FloatingActionButton.java b/design/src/android/support/design/widget/FloatingActionButton.java
index bd2ce10..13262a2 100644
--- a/design/src/android/support/design/widget/FloatingActionButton.java
+++ b/design/src/android/support/design/widget/FloatingActionButton.java
@@ -27,10 +27,8 @@
 import android.support.annotation.Nullable;
 import android.support.design.R;
 import android.support.v4.view.ViewCompat;
-import android.support.v4.view.ViewPropertyAnimatorListener;
 import android.util.AttributeSet;
 import android.view.View;
-import android.view.animation.Animation;
 import android.widget.ImageView;
 
 import java.util.List;
@@ -119,8 +117,11 @@
             }
         };
 
-        if (Build.VERSION.SDK_INT >= 21) {
+        final int sdk = Build.VERSION.SDK_INT;
+        if (sdk >= 21) {
             mImpl = new FloatingActionButtonLollipop(this, delegate);
+        } else if (sdk >= 12) {
+            mImpl = new FloatingActionButtonHoneycombMr1(this, delegate);
         } else {
             mImpl = new FloatingActionButtonEclairMr1(this, delegate);
         }
@@ -220,7 +221,36 @@
     public void setBackgroundDrawable(Drawable background) {
         if (mImpl != null) {
             mImpl.setBackgroundDrawable(
-                background, mBackgroundTint, mBackgroundTintMode, mRippleColor, mBorderWidth);
+                    background, mBackgroundTint, mBackgroundTintMode, mRippleColor, mBorderWidth);
+        }
+    }
+
+    /**
+     * Shows the button.
+     * <p>This method will animate it the button show if the view has already been laid out.</p>
+     */
+    public void show() {
+        if (getVisibility() == VISIBLE) {
+            return;
+        }
+        setVisibility(VISIBLE);
+        if (ViewCompat.isLaidOut(this)) {
+            mImpl.show();
+        }
+    }
+
+    /**
+     * Hides the button.
+     * <p>This method will animate the button hide if the view has already been laid out.</p>
+     */
+    public void hide() {
+        if (getVisibility() != VISIBLE) {
+            return;
+        }
+        if (ViewCompat.isLaidOut(this)) {
+            mImpl.hide();
+        } else {
+            setVisibility(GONE);
         }
     }
 
@@ -299,13 +329,11 @@
         private static final boolean SNACKBAR_BEHAVIOR_ENABLED = Build.VERSION.SDK_INT >= 11;
 
         private Rect mTmpRect;
-        private boolean mIsAnimatingOut;
         private float mTranslationY;
 
         @Override
         public boolean layoutDependsOn(CoordinatorLayout parent,
-                FloatingActionButton child,
-                View dependency) {
+                FloatingActionButton child, View dependency) {
             // We're dependent on all SnackbarLayouts (if enabled)
             return SNACKBAR_BEHAVIOR_ENABLED && dependency instanceof Snackbar.SnackbarLayout;
         }
@@ -316,32 +344,47 @@
             if (dependency instanceof Snackbar.SnackbarLayout) {
                 updateFabTranslationForSnackbar(parent, child, dependency);
             } else if (dependency instanceof AppBarLayout) {
-                final AppBarLayout appBarLayout = (AppBarLayout) dependency;
-                if (mTmpRect == null) {
-                    mTmpRect = new Rect();
-                }
-
-                // First, let's get the visible rect of the dependency
-                final Rect rect = mTmpRect;
-                ViewGroupUtils.getDescendantRect(parent, dependency, rect);
-
-                if (rect.bottom <= appBarLayout.getMinimumHeightForVisibleOverlappingContent()) {
-                    // If the anchor's bottom is below the seam, we'll animate our FAB out
-                    if (!mIsAnimatingOut && child.getVisibility() == View.VISIBLE) {
-                        animateOut(child);
-                    }
-                } else {
-                    // Else, we'll animate our FAB back in
-                    if (child.getVisibility() != View.VISIBLE) {
-                        animateIn(child);
-                    }
-                }
+                // If we're depending on an AppBarLayout we will show/hide it automatically
+                // if the FAB is anchored to the AppBarLayout
+                updateFabVisibility(parent, (AppBarLayout) dependency, child);
             }
             return false;
         }
 
+        private boolean updateFabVisibility(CoordinatorLayout parent,
+                AppBarLayout appBarLayout, FloatingActionButton child) {
+            final CoordinatorLayout.LayoutParams lp =
+                    (CoordinatorLayout.LayoutParams) child.getLayoutParams();
+            if (lp.getAnchorId() != appBarLayout.getId()) {
+                // The anchor ID doesn't match the dependency, so we won't automatically
+                // show/hide the FAB
+                return false;
+            }
+
+            if (mTmpRect == null) {
+                mTmpRect = new Rect();
+            }
+
+            // First, let's get the visible rect of the dependency
+            final Rect rect = mTmpRect;
+            ViewGroupUtils.getDescendantRect(parent, appBarLayout, rect);
+
+            if (rect.bottom <= appBarLayout.getMinimumHeightForVisibleOverlappingContent()) {
+                // If the anchor's bottom is below the seam, we'll animate our FAB out
+                child.hide();
+            } else {
+                // Else, we'll animate our FAB back in
+                child.show();
+            }
+            return true;
+        }
+
         private void updateFabTranslationForSnackbar(CoordinatorLayout parent,
                 FloatingActionButton fab, View snackbar) {
+            if (fab.getVisibility() != View.VISIBLE) {
+                return;
+            }
+
             final float translationY = getFabTranslationYForSnackbar(parent, fab);
             if (translationY != mTranslationY) {
                 // First, cancel any current animation
@@ -377,83 +420,25 @@
             return minOffset;
         }
 
-        private void animateIn(FloatingActionButton button) {
-            button.setVisibility(View.VISIBLE);
-
-            if (Build.VERSION.SDK_INT >= 14) {
-                ViewCompat.animate(button)
-                        .scaleX(1f)
-                        .scaleY(1f)
-                        .alpha(1f)
-                        .setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR)
-                        .withLayer()
-                        .setListener(null)
-                        .start();
-            } else {
-                Animation anim = android.view.animation.AnimationUtils.loadAnimation(
-                        button.getContext(), R.anim.fab_in);
-                anim.setDuration(200);
-                anim.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
-                button.startAnimation(anim);
-            }
-        }
-
         @Override
         public boolean onLayoutChild(CoordinatorLayout parent, FloatingActionButton child,
                 int layoutDirection) {
-            // Let the CoordinatorLayout lay out the FAB
+            // First, lets make sure that the visibility of the FAB is consistent
+            final List<View> dependencies = parent.getDependencies(child);
+            for (int i = 0, count = dependencies.size(); i < count; i++) {
+                final View dependency = dependencies.get(i);
+                if (dependency instanceof AppBarLayout
+                        && updateFabVisibility(parent, (AppBarLayout) dependency, child)) {
+                    break;
+                }
+            }
+            // Now let the CoordinatorLayout lay out the FAB
             parent.onLayoutChild(child, layoutDirection);
             // Now offset it if needed
             offsetIfNeeded(parent, child);
             return true;
         }
 
-        private void animateOut(final FloatingActionButton button) {
-            if (Build.VERSION.SDK_INT >= 14) {
-                ViewCompat.animate(button)
-                        .scaleX(0f)
-                        .scaleY(0f)
-                        .alpha(0f)
-                        .setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR)
-                        .withLayer()
-                        .setListener(new ViewPropertyAnimatorListener() {
-                            @Override
-                            public void onAnimationStart(View view) {
-                                mIsAnimatingOut = true;
-                            }
-
-                            @Override
-                            public void onAnimationCancel(View view) {
-                                mIsAnimatingOut = false;
-                            }
-
-                            @Override
-                            public void onAnimationEnd(View view) {
-                                mIsAnimatingOut = false;
-                                view.setVisibility(View.GONE);
-                            }
-                        }).start();
-            } else {
-                Animation anim = android.view.animation.AnimationUtils.loadAnimation(
-                        button.getContext(), R.anim.fab_out);
-                anim.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
-                anim.setDuration(200);
-                anim.setAnimationListener(new AnimationUtils.AnimationListenerAdapter() {
-                    @Override
-                    public void onAnimationStart(Animation animation) {
-                        mIsAnimatingOut = true;
-                    }
-
-                    @Override
-                    public void onAnimationEnd(Animation animation) {
-                        mIsAnimatingOut = false;
-                        button.setVisibility(View.GONE);
-                    }
-                });
-                button.startAnimation(anim);
-            }
-        }
-
         /**
          * Pre-Lollipop we use padding so that the shadow has enough space to be drawn. This method
          * offsets our layout position so that we're positioned correctly if we're on one of
diff --git a/design/src/android/support/design/widget/TabLayout.java b/design/src/android/support/design/widget/TabLayout.java
index f2e44ec..6e0153a 100755
--- a/design/src/android/support/design/widget/TabLayout.java
+++ b/design/src/android/support/design/widget/TabLayout.java
@@ -25,7 +25,12 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.support.annotation.ColorInt;
+import android.support.annotation.DrawableRes;
 import android.support.annotation.IntDef;
+import android.support.annotation.LayoutRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.StringRes;
 import android.support.design.R;
 import android.support.v4.view.GravityCompat;
 import android.support.v4.view.PagerAdapter;
@@ -33,7 +38,6 @@
 import android.support.v4.view.ViewPager;
 import android.support.v7.app.ActionBar;
 import android.support.v7.internal.widget.TintManager;
-import android.support.v7.widget.AppCompatTextView;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.Gravity;
@@ -330,7 +334,7 @@
      *
      * @param tab Tab to add
      */
-    public void addTab(Tab tab) {
+    public void addTab(@NonNull Tab tab) {
         addTab(tab, mTabs.isEmpty());
     }
 
@@ -341,7 +345,7 @@
      * @param tab The tab to add
      * @param position The new position of the tab
      */
-    public void addTab(Tab tab, int position) {
+    public void addTab(@NonNull Tab tab, int position) {
         addTab(tab, position, mTabs.isEmpty());
     }
 
@@ -351,7 +355,7 @@
      * @param tab Tab to add
      * @param setSelected True if the added tab should become the selected tab.
      */
-    public void addTab(Tab tab, boolean setSelected) {
+    public void addTab(@NonNull Tab tab, boolean setSelected) {
         if (tab.mParent != this) {
             throw new IllegalArgumentException("Tab belongs to a different TabLayout.");
         }
@@ -370,7 +374,7 @@
      * @param position The new position of the tab
      * @param setSelected True if the added tab should become the selected tab.
      */
-    public void addTab(Tab tab, int position, boolean setSelected) {
+    public void addTab(@NonNull Tab tab, int position, boolean setSelected) {
         if (tab.mParent != this) {
             throw new IllegalArgumentException("Tab belongs to a different TabLayout.");
         }
@@ -399,6 +403,7 @@
      * @return A new Tab
      * @see #addTab(Tab)
      */
+    @NonNull
     public Tab newTab() {
         return new Tab(this);
     }
@@ -415,6 +420,7 @@
     /**
      * Returns the tab at the specified index.
      */
+    @Nullable
     public Tab getTabAt(int index) {
         return mTabs.get(index);
     }
@@ -529,7 +535,7 @@
     /**
      * Sets the text colors for the different states (normal, selected) used for the tabs.
      */
-    public void setTabTextColors(ColorStateList textColor) {
+    public void setTabTextColors(@Nullable ColorStateList textColor) {
         if (mTabTextColors != textColor) {
             mTabTextColors = textColor;
             updateAllTabs();
@@ -539,6 +545,7 @@
     /**
      * Gets the text colors for the different states (normal, selected) used for the tabs.
      */
+    @Nullable
     public ColorStateList getTabTextColors() {
         return mTabTextColors;
     }
@@ -567,7 +574,7 @@
      * @see TabLayoutOnPageChangeListener
      * @see ViewPagerOnTabSelectedListener
      */
-    public void setupWithViewPager(ViewPager viewPager) {
+    public void setupWithViewPager(@NonNull ViewPager viewPager) {
         final PagerAdapter adapter = viewPager.getAdapter();
         if (adapter == null) {
             throw new IllegalArgumentException("ViewPager does not have a PagerAdapter set");
@@ -597,7 +604,7 @@
      *
      * @param adapter the adapter to populate from
      */
-    public void setTabsFromPagerAdapter(PagerAdapter adapter) {
+    public void setTabsFromPagerAdapter(@NonNull PagerAdapter adapter) {
         removeAllTabs();
         for (int i = 0, count = adapter.getCount(); i < count; i++) {
             addTab(newTab().setText(adapter.getPageTitle(i)));
@@ -880,6 +887,7 @@
         /**
          * @return This Tab's tag object.
          */
+        @Nullable
         public Object getTag() {
             return mTag;
         }
@@ -890,7 +898,8 @@
          * @param tag Object to store
          * @return The current instance for call chaining
          */
-        public Tab setTag(Object tag) {
+        @NonNull
+        public Tab setTag(@Nullable Object tag) {
             mTag = tag;
             return this;
         }
@@ -900,13 +909,20 @@
         }
 
         /**
-         * Set a custom view to be used for this tab. This overrides values set by {@link
-         * #setText(CharSequence)} and {@link #setIcon(Drawable)}.
+         * Set a custom view to be used for this tab.
+         * <p>
+         * If the provided view contains a {@link TextView} with an ID of
+         * {@link android.R.id#text1} then that will be updated with the value given
+         * to {@link #setText(CharSequence)}. Similarly, if this layout contains an
+         * {@link ImageView} with ID {@link android.R.id#icon} then it will be updated with
+         * the value given to {@link #setIcon(Drawable)}.
+         * </p>
          *
          * @param view Custom view to be used as a tab.
          * @return The current instance for call chaining
          */
-        public Tab setCustomView(View view) {
+        @NonNull
+        public Tab setCustomView(@Nullable View view) {
             mCustomView = view;
             if (mPosition >= 0) {
                 mParent.updateTab(mPosition);
@@ -915,13 +931,20 @@
         }
 
         /**
-         * Set a custom view to be used for this tab. This overrides values set by {@link
-         * #setText(CharSequence)} and {@link #setIcon(Drawable)}.
+         * Set a custom view to be used for this tab.
+         * <p>
+         * If the inflated layout contains a {@link TextView} with an ID of
+         * {@link android.R.id#text1} then that will be updated with the value given
+         * to {@link #setText(CharSequence)}. Similarly, if this layout contains an
+         * {@link ImageView} with ID {@link android.R.id#icon} then it will be updated with
+         * the value given to {@link #setIcon(Drawable)}.
+         * </p>
          *
          * @param layoutResId A layout resource to inflate and use as a custom tab view
          * @return The current instance for call chaining
          */
-        public Tab setCustomView(int layoutResId) {
+        @NonNull
+        public Tab setCustomView(@LayoutRes int layoutResId) {
             return setCustomView(
                     LayoutInflater.from(mParent.getContext()).inflate(layoutResId, null));
         }
@@ -931,6 +954,7 @@
          *
          * @return The tab's icon
          */
+        @Nullable
         public Drawable getIcon() {
             return mIcon;
         }
@@ -954,6 +978,7 @@
          *
          * @return The tab's text
          */
+        @Nullable
         public CharSequence getText() {
             return mText;
         }
@@ -964,7 +989,8 @@
          * @param icon The drawable to use as an icon
          * @return The current instance for call chaining
          */
-        public Tab setIcon(Drawable icon) {
+        @NonNull
+        public Tab setIcon(@Nullable Drawable icon) {
             mIcon = icon;
             if (mPosition >= 0) {
                 mParent.updateTab(mPosition);
@@ -978,7 +1004,8 @@
          * @param resId A resource ID referring to the icon that should be displayed
          * @return The current instance for call chaining
          */
-        public Tab setIcon(int resId) {
+        @NonNull
+        public Tab setIcon(@DrawableRes int resId) {
             return setIcon(TintManager.getDrawable(mParent.getContext(), resId));
         }
 
@@ -989,7 +1016,8 @@
          * @param text The text to display
          * @return The current instance for call chaining
          */
-        public Tab setText(CharSequence text) {
+        @NonNull
+        public Tab setText(@Nullable CharSequence text) {
             mText = text;
             if (mPosition >= 0) {
                 mParent.updateTab(mPosition);
@@ -1004,7 +1032,8 @@
          * @param resId A resource ID referring to the text that should be displayed
          * @return The current instance for call chaining
          */
-        public Tab setText(int resId) {
+        @NonNull
+        public Tab setText(@StringRes int resId) {
             return setText(mParent.getResources().getText(resId));
         }
 
@@ -1024,7 +1053,8 @@
          * @see #setContentDescription(CharSequence)
          * @see #getContentDescription()
          */
-        public Tab setContentDescription(int resId) {
+        @NonNull
+        public Tab setContentDescription(@StringRes int resId) {
             return setContentDescription(mParent.getResources().getText(resId));
         }
 
@@ -1037,7 +1067,8 @@
          * @see #setContentDescription(int)
          * @see #getContentDescription()
          */
-        public Tab setContentDescription(CharSequence contentDesc) {
+        @NonNull
+        public Tab setContentDescription(@Nullable CharSequence contentDesc) {
             mContentDesc = contentDesc;
             if (mPosition >= 0) {
                 mParent.updateTab(mPosition);
@@ -1052,6 +1083,7 @@
          * @see #setContentDescription(CharSequence)
          * @see #setContentDescription(int)
          */
+        @Nullable
         public CharSequence getContentDescription() {
             return mContentDesc;
         }
@@ -1061,7 +1093,10 @@
         private final Tab mTab;
         private TextView mTextView;
         private ImageView mIconView;
+
         private View mCustomView;
+        private TextView mCustomTextView;
+        private ImageView mCustomIconView;
 
         public TabView(Context context, Tab tab) {
             super(context);
@@ -1141,64 +1176,80 @@
                     mIconView.setVisibility(GONE);
                     mIconView.setImageDrawable(null);
                 }
+
+                mCustomTextView = (TextView) custom.findViewById(android.R.id.text1);
+                mCustomIconView = (ImageView) custom.findViewById(android.R.id.icon);
             } else {
+                // We do not have a custom view. Remove one if it already exists
                 if (mCustomView != null) {
                     removeView(mCustomView);
                     mCustomView = null;
                 }
+                mCustomTextView = null;
+                mCustomIconView = null;
+            }
 
-                final Drawable icon = tab.getIcon();
-                final CharSequence text = tab.getText();
+            if (mCustomView == null) {
+                // If there isn't a custom view, we'll us our own in-built layouts
+                if (mIconView == null) {
+                    ImageView iconView = (ImageView) LayoutInflater.from(getContext())
+                            .inflate(R.layout.layout_tab_icon, this, false);
+                    addView(iconView, 0);
+                    mIconView = iconView;
+                }
+                if (mTextView == null) {
+                    TextView textView = (TextView) LayoutInflater.from(getContext())
+                            .inflate(R.layout.layout_tab_text, this, false);
+                    addView(textView);
+                    mTextView = textView;
+                }
+                mTextView.setTextAppearance(getContext(), mTabTextAppearance);
+                if (mTabTextColors != null) {
+                    mTextView.setTextColor(mTabTextColors);
+                }
+                updateTextAndIcon(tab, mTextView, mIconView);
+            } else {
+                // Else, we'll see if there is a TextView or ImageView present and update them
+                if (mCustomTextView != null || mCustomIconView != null) {
+                    updateTextAndIcon(tab, mCustomTextView, mCustomIconView);
+                }
+            }
+        }
 
+        private void updateTextAndIcon(Tab tab, TextView textView, ImageView iconView) {
+            final Drawable icon = tab.getIcon();
+            final CharSequence text = tab.getText();
+
+            if (iconView != null) {
                 if (icon != null) {
-                    if (mIconView == null) {
-                        ImageView iconView = new ImageView(getContext());
-                        LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
-                                LayoutParams.WRAP_CONTENT);
-                        lp.gravity = Gravity.CENTER_VERTICAL;
-                        iconView.setLayoutParams(lp);
-                        addView(iconView, 0);
-                        mIconView = iconView;
-                    }
-                    mIconView.setImageDrawable(icon);
-                    mIconView.setVisibility(VISIBLE);
-                } else if (mIconView != null) {
-                    mIconView.setVisibility(GONE);
-                    mIconView.setImageDrawable(null);
-                }
-
-                final boolean hasText = !TextUtils.isEmpty(text);
-                if (hasText) {
-                    if (mTextView == null) {
-                        AppCompatTextView textView = new AppCompatTextView(getContext());
-                        textView.setMaxLines(MAX_TAB_TEXT_LINES);
-                        textView.setEllipsize(TextUtils.TruncateAt.END);
-                        textView.setGravity(Gravity.CENTER);
-                        addView(textView, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
-                        mTextView = textView;
-                    }
-                    mTextView.setTextAppearance(getContext(), mTabTextAppearance);
-                    if (mTabTextColors != null) {
-                        mTextView.setTextColor(mTabTextColors);
-                    }
-                    mTextView.setText(text);
-                    mTextView.setContentDescription(tab.getContentDescription());
-                    mTextView.setVisibility(VISIBLE);
-                } else if (mTextView != null) {
-                    mTextView.setVisibility(GONE);
-                    mTextView.setText(null);
-                }
-
-                if (mIconView != null) {
-                    mIconView.setContentDescription(tab.getContentDescription());
-                }
-
-                if (!hasText && !TextUtils.isEmpty(tab.getContentDescription())) {
-                    setOnLongClickListener(this);
+                    iconView.setImageDrawable(icon);
+                    iconView.setVisibility(VISIBLE);
+                    setVisibility(VISIBLE);
                 } else {
-                    setOnLongClickListener(null);
-                    setLongClickable(false);
+                    iconView.setVisibility(GONE);
+                    iconView.setImageDrawable(null);
                 }
+                iconView.setContentDescription(tab.getContentDescription());
+            }
+
+            final boolean hasText = !TextUtils.isEmpty(text);
+            if (textView != null) {
+                if (hasText) {
+                    textView.setText(text);
+                    textView.setContentDescription(tab.getContentDescription());
+                    textView.setVisibility(VISIBLE);
+                    setVisibility(VISIBLE);
+                } else {
+                    textView.setVisibility(GONE);
+                    textView.setText(null);
+                }
+            }
+
+            if (!hasText && !TextUtils.isEmpty(tab.getContentDescription())) {
+                setOnLongClickListener(this);
+            } else {
+                setOnLongClickListener(null);
+                setLongClickable(false);
             }
         }
 
diff --git a/v7/appcompat/api/current.txt b/v7/appcompat/api/current.txt
index cf6bc5c..fd3f7c3 100644
--- a/v7/appcompat/api/current.txt
+++ b/v7/appcompat/api/current.txt
@@ -250,6 +250,7 @@
     method public abstract android.support.v7.app.ActionBarDrawerToggle.Delegate getDrawerToggleDelegate();
     method public abstract android.view.MenuInflater getMenuInflater();
     method public abstract android.support.v7.app.ActionBar getSupportActionBar();
+    method public abstract boolean hasWindowFeature(int);
     method public abstract void installViewFactory();
     method public abstract void invalidateOptionsMenu();
     method public abstract boolean isHandleNativeActionModesEnabled();
diff --git a/v7/appcompat/res/values/themes_base.xml b/v7/appcompat/res/values/themes_base.xml
index 3e2c841..5db9d05 100644
--- a/v7/appcompat/res/values/themes_base.xml
+++ b/v7/appcompat/res/values/themes_base.xml
@@ -571,7 +571,7 @@
         <item name="isLightTheme">true</item>
     </style>
 
-    <style name="Base.ThemeOverlay.AppCompat.Dark">
+    <style name="Base.ThemeOverlay.AppCompat.Dark" parent="Platform.ThemeOverlay.AppCompat.Dark">
         <item name="android:windowBackground">@color/background_material_dark</item>
         <item name="android:colorForeground">@color/bright_foreground_material_dark</item>
         <item name="android:colorForegroundInverse">@color/bright_foreground_material_light</item>
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java b/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java
index 8dbc827..e4283e3 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java
@@ -55,8 +55,8 @@
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         getDelegate().installViewFactory();
-        super.onCreate(savedInstanceState);
         getDelegate().onCreate(savedInstanceState);
+        super.onCreate(savedInstanceState);
     }
 
     @Override
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegate.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegate.java
index 8317ad2..3abe072 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDelegate.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegate.java
@@ -214,6 +214,16 @@
     public abstract boolean requestWindowFeature(int featureId);
 
     /**
+     * Query for the availability of a certain feature.
+     *
+     * <p>This should be called instead of {@link android.view.Window#hasFeature(int)}.</p>
+     *
+     * @param featureId The feature ID to check
+     * @return true if the feature is enabled, false otherwise.
+     */
+    public abstract boolean hasWindowFeature(int featureId);
+
+    /**
      * Start an action mode.
      *
      * @param callback Callback that will manage lifecycle events for this context mode
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV7.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV7.java
index abb665f..c777f57 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV7.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV7.java
@@ -503,6 +503,25 @@
     }
 
     @Override
+    public boolean hasWindowFeature(int featureId) {
+        switch (featureId) {
+            case FEATURE_ACTION_BAR:
+                return mHasActionBar;
+            case FEATURE_ACTION_BAR_OVERLAY:
+                return mOverlayActionBar;
+            case FEATURE_ACTION_MODE_OVERLAY:
+                return mOverlayActionMode;
+            case Window.FEATURE_PROGRESS:
+                return mFeatureProgress;
+            case Window.FEATURE_INDETERMINATE_PROGRESS:
+                return mFeatureIndeterminateProgress;
+            case Window.FEATURE_NO_TITLE:
+                return mWindowNoTitle;
+        }
+        return mWindow.hasFeature(featureId);
+    }
+
+    @Override
     void onTitleChanged(CharSequence title) {
         if (mDecorContentParent != null) {
             mDecorContentParent.setWindowTitle(title);
@@ -1105,8 +1124,10 @@
             mDecorContentParent.setMenuPrepared();
         }
 
-        if (st.createdPanelView == null) {
-            // Init the panel state's menu--return false if init failed
+        if (st.createdPanelView == null &&
+                (!isActionBarMenu || !(peekSupportActionBar() instanceof ToolbarActionBar))) {
+            // Since ToolbarActionBar handles the list options menu itself, we only want to
+            // init this menu panel if we're not using a TAB.
             if (st.menu == null || st.refreshMenuContent) {
                 if (st.menu == null) {
                     if (!initializePanelMenu(st) || (st.menu == null)) {
diff --git a/v7/appcompat/src/android/support/v7/internal/view/menu/ActionMenuItemView.java b/v7/appcompat/src/android/support/v7/internal/view/menu/ActionMenuItemView.java
index 05c92c9..a6c6dde 100644
--- a/v7/appcompat/src/android/support/v7/internal/view/menu/ActionMenuItemView.java
+++ b/v7/appcompat/src/android/support/v7/internal/view/menu/ActionMenuItemView.java
@@ -250,7 +250,8 @@
         Toast cheatSheet = Toast.makeText(context, mItemData.getTitle(), Toast.LENGTH_SHORT);
         if (midy < displayFrame.height()) {
             // Show along the top; follow action buttons
-            cheatSheet.setGravity(Gravity.TOP | GravityCompat.END, referenceX, height);
+            cheatSheet.setGravity(Gravity.TOP | GravityCompat.END, referenceX,
+                    screenPos[1] + height - displayFrame.top);
         } else {
             // Show along the bottom center
             cheatSheet.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, height);