Add more fixes to BottomNavigationView.

- Fixes crash for fast taps of buttons where
  BottomNavigationItemView#getAnimator returns null since the view
  is already in the correct state.
- Added checks for illegal menus
  - Max menu item check 5 items
  - No submenus
- Updated the sizing of the BottomNavigationItemView.
- Adding missing getItemBackgroundResource method.

Bug: 27675079

Change-Id: I856d250d92e69925a75cf4996a1d02f91441d67c
diff --git a/api/current.txt b/api/current.txt
index a28ee73..36b2296 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -314,8 +314,10 @@
     ctor public BottomNavigationView(android.content.Context);
     ctor public BottomNavigationView(android.content.Context, android.util.AttributeSet);
     ctor public BottomNavigationView(android.content.Context, android.util.AttributeSet, int);
+    method public int getItemBackgroundResource();
     method public android.content.res.ColorStateList getItemIconTintList();
     method public android.content.res.ColorStateList getItemTextColor();
+    method public int getMaxItemCount();
     method public android.view.Menu getMenu();
     method public void inflateMenu(int);
     method public void setItemBackgroundResource(int);
diff --git a/design/src/android/support/design/internal/BottomNavigationMenu.java b/design/src/android/support/design/internal/BottomNavigationMenu.java
new file mode 100644
index 0000000..d813ea2
--- /dev/null
+++ b/design/src/android/support/design/internal/BottomNavigationMenu.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2016 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.internal;
+
+import android.content.Context;
+import android.support.v7.view.menu.MenuBuilder;
+import android.view.MenuItem;
+import android.view.SubMenu;
+
+/**
+ * @hide
+ */
+public final class BottomNavigationMenu extends MenuBuilder {
+    public static final int MAX_ITEM_COUNT = 5;
+
+    public BottomNavigationMenu(Context context) {
+        super(context);
+    }
+
+    @Override
+    public SubMenu addSubMenu(int group, int id, int categoryOrder, CharSequence title) {
+        throw new UnsupportedOperationException("BottomNavigationView does not support submenus");
+    }
+
+    @Override
+    protected MenuItem addInternal(int group, int id, int categoryOrder, CharSequence title) {
+        if (size() + 1 > MAX_ITEM_COUNT) {
+            throw new IllegalArgumentException(
+                    "Maximum number of items supported by BottomNavigationView is " + MAX_ITEM_COUNT
+                            + ". Limit can be checked with BottomNavigationView#getMaxItemCount()");
+        }
+        return super.addInternal(group, id, categoryOrder, title);
+    }
+}
diff --git a/design/src/android/support/design/internal/BottomNavigationMenuView.java b/design/src/android/support/design/internal/BottomNavigationMenuView.java
index c7c29f0..51750ac 100644
--- a/design/src/android/support/design/internal/BottomNavigationMenuView.java
+++ b/design/src/android/support/design/internal/BottomNavigationMenuView.java
@@ -16,6 +16,7 @@
 
 package android.support.design.internal;
 
+import android.animation.Animator;
 import android.animation.AnimatorSet;
 import android.content.Context;
 import android.content.res.ColorStateList;
@@ -177,9 +178,14 @@
         if (mActiveButton == newButton) return;
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
             AnimatorSet animatorSet = new AnimatorSet();
-            animatorSet.playTogether(
-                    mButtons[mActiveButton].getAnimator(false),
-                    mButtons[newButton].getAnimator(true));
+            Animator animatorUnfocus = mButtons[mActiveButton].getAnimator(false);
+            Animator animatorFocus = mButtons[newButton].getAnimator(true);
+            if (animatorUnfocus != null) {
+                animatorSet.play(animatorUnfocus);
+            }
+            if (animatorFocus != null) {
+                animatorSet.play(animatorFocus);
+            }
             animatorSet.start();
         }
         mPresenter.setUpdateSuspended(true);
@@ -194,20 +200,26 @@
         mActiveButton = newButton;
     }
 
-    public void updateOnSizeChange(int width) {
-        if (getChildCount() == 0) return;
+    public boolean updateOnSizeChange(int width) {
+        if (getChildCount() == 0) {
+            return false;
+        }
         int available = width / getChildCount();
         int itemWidth = Math.min(available, mActiveItemMaxWidth);
 
+        boolean changed = false;
+
         for (int i = 0; i < mButtons.length; i++) {
             ViewGroup.LayoutParams params = mButtons[i].getLayoutParams();
             if (params.width == itemWidth) {
                 continue;
             }
+            changed = true;
             params.width = itemWidth;
             params.height = ViewGroup.LayoutParams.MATCH_PARENT;
             mButtons[i].setLayoutParams(params);
         }
+        return changed;
     }
 
     private BottomNavigationItemView getNewItem() {
diff --git a/design/src/android/support/design/widget/BottomNavigationView.java b/design/src/android/support/design/widget/BottomNavigationView.java
index 910e667..c271757 100644
--- a/design/src/android/support/design/widget/BottomNavigationView.java
+++ b/design/src/android/support/design/widget/BottomNavigationView.java
@@ -18,10 +18,12 @@
 
 import android.content.Context;
 import android.content.res.ColorStateList;
+import android.os.Parcelable;
 import android.support.annotation.DrawableRes;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.design.R;
+import android.support.design.internal.BottomNavigationMenu;
 import android.support.design.internal.BottomNavigationMenuView;
 import android.support.design.internal.BottomNavigationPresenter;
 import android.support.v7.content.res.AppCompatResources;
@@ -82,7 +84,7 @@
         ThemeUtils.checkAppCompatTheme(context);
 
         // Create the menu
-        mMenu = new MenuBuilder(context);
+        mMenu = new BottomNavigationMenu(context);
 
         mMenuView = new BottomNavigationMenuView(context);
         LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
@@ -145,17 +147,12 @@
         mListener = listener;
     }
 
-    @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        // TODO(aurimas): move updateOnSizeChange to a different location that is less expensive.
-        mMenuView.updateOnSizeChange(MeasureSpec.getSize(widthMeasureSpec));
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-    }
-
-    @Override
-    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        super.onSizeChanged(w, h, oldw, oldh);
-        mMenuView.updateOnSizeChange(w);
+        if (mMenuView.updateOnSizeChange(getMeasuredWidth())) {
+            // updateOnSizeChanged has changed LPs, so we need to remeasure
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        }
     }
 
     /**
@@ -182,6 +179,13 @@
     }
 
     /**
+     * @return The maximum number of items that can be shown in BottomNavigationView.
+     */
+    public int getMaxItemCount() {
+        return BottomNavigationMenu.MAX_ITEM_COUNT;
+    }
+
+    /**
      * Returns the tint which is applied to our menu items' icons.
      *
      * @see #setItemIconTintList(ColorStateList)
@@ -204,7 +208,6 @@
         mMenuView.setIconTintList(tint);
     }
 
-
     /**
      * Returns the tint which is applied to menu items' icons.
      *
@@ -229,6 +232,18 @@
     }
 
     /**
+     * Returns the background resource of the menu items.
+     *
+     * @see #setItemBackgroundResource(int)
+     *
+     * @attr ref R.styleable#BottomNavigationView_itemBackground
+     */
+    @DrawableRes
+    public int getItemBackgroundResource() {
+        return mMenuView.getItemBackgroundRes();
+    }
+
+    /**
      * Set the background of our menu items to the given resource.
      *
      * @param resId The identifier of the resource.