Allow making search and settings MenuItems via XML

Fixes: 144957101
Test: Manually
Change-Id: I4fdb6d2107b05b5fb9044aa38de76dc3b2b4789f
diff --git a/car-ui-lib/res/values/attrs.xml b/car-ui-lib/res/values/attrs.xml
index 4c17d38..71ad091 100644
--- a/car-ui-lib/res/values/attrs.xml
+++ b/car-ui-lib/res/values/attrs.xml
@@ -46,6 +46,10 @@
         <attr name="id" format="reference"/>
         <!-- Show/hide the MenuItem -->
         <attr name="visible" format="boolean"/>
+        <!-- Set this to true to make a search MenuItem. This will override every other property except id, visible, and onclick. -->
+        <attr name="search" format="boolean"/>
+        <!-- Set this to true to make a settings MenuItem. This will override every other property except id, visible, and onclick. -->
+        <attr name="settings" format="boolean"/>
         <!-- Title -->
         <attr name="title"/>
         <!-- Icon -->
diff --git a/car-ui-lib/src/com/android/car/ui/toolbar/MenuItem.java b/car-ui-lib/src/com/android/car/ui/toolbar/MenuItem.java
index 935a9db..39e2e15 100644
--- a/car-ui-lib/src/com/android/car/ui/toolbar/MenuItem.java
+++ b/car-ui-lib/src/com/android/car/ui/toolbar/MenuItem.java
@@ -40,8 +40,8 @@
  * itself, or it's overflow menu.
  *
  * <p>If you require a search or settings button, you should use
- * {@link Builder#createSearch(Context, OnClickListener)} or
- * {@link Builder#createSettings(Context, OnClickListener)}.
+ * {@link Builder#setToSearch()} or
+ * {@link Builder#setToSettings()}.
  *
  * <p>Some properties can be changed after the creating a MenuItem, but others require being set
  * with a {@link Builder}.
@@ -297,15 +297,13 @@
         return mIsSearch;
     }
 
-    /**
-     * Builder class.
-     *
-     * <p>Use the static {@link #createSearch(Context, OnClickListener)} or
-     * {@link #createSettings(Context, OnClickListener)} if you want one of those specialized
-     * buttons.
-     */
+    /** Builder class */
     public static final class Builder {
-        private Context mContext;
+        private final Context mContext;
+        private final CharSequence mSearchTitle;
+        private final CharSequence mSettingsTitle;
+        private final Drawable mSearchIcon;
+        private final Drawable mSettingsIcon;
 
         private int mId = View.NO_ID;
         private CharSequence mTitle;
@@ -321,6 +319,7 @@
         private boolean mIsActivatable = false;
         private boolean mIsActivated = false;
         private boolean mIsSearch = false;
+        private boolean mIsSettings = false;
         @CarUxRestrictions.CarUxRestrictionsInfo
         private int mUxRestrictions = CarUxRestrictions.UX_RESTRICTIONS_BASELINE;
 
@@ -328,6 +327,10 @@
             // Must use getApplicationContext to avoid leaking activities when the MenuItem
             // is held onto for longer than the Activity's lifecycle
             mContext = c.getApplicationContext();
+            mSearchTitle = mContext.getString(R.string.car_ui_toolbar_menu_item_search_title);
+            mSettingsTitle = mContext.getString(R.string.car_ui_toolbar_menu_item_settings_title);
+            mSearchIcon = mContext.getDrawable(R.drawable.car_ui_icon_search);
+            mSettingsIcon = mContext.getDrawable(R.drawable.car_ui_icon_settings);
         }
 
         /** Builds a {@link MenuItem} from the current state of the Builder */
@@ -341,6 +344,30 @@
                     || mIsActivatable)) {
                 throw new IllegalStateException("Unsupported options for a checkable MenuItem");
             }
+            if (mIsSearch && mIsSettings) {
+                throw new IllegalStateException("Can't have both a search and settings MenuItem");
+            }
+
+            if (mIsSearch && (!mSearchTitle.equals(mTitle)
+                    || !mSearchIcon.equals(mIcon)
+                    || mIsCheckable
+                    || mIsActivatable
+                    || !mIsTinted
+                    || mShowIconAndTitle
+                    || mDisplayBehavior != DisplayBehavior.ALWAYS)) {
+                throw new IllegalStateException("Invalid search MenuItem");
+            }
+
+            if (mIsSettings && (!mSettingsTitle.equals(mTitle)
+                    || !mSettingsIcon.equals(mIcon)
+                    || mIsCheckable
+                    || mIsActivatable
+                    || !mIsTinted
+                    || mShowIconAndTitle
+                    || mDisplayBehavior != DisplayBehavior.ALWAYS
+                    || mUxRestrictions != CarUxRestrictions.UX_RESTRICTIONS_NO_SETUP)) {
+                throw new IllegalStateException("Invalid settings MenuItem");
+            }
 
             return new MenuItem(this);
         }
@@ -486,26 +513,20 @@
             return this;
         }
 
-        /** Sets that this is the search MenuItem, which has special behavior while searching */
-        private Builder setSearch() {
-            mIsSearch = true;
-            return this;
-        }
-
         /**
          * Creates a search MenuItem.
          *
          * <p>The advantage of using this over creating your own is getting an OEM-styled search
          * icon, and this button will always disappear while searching, even when the
          * {@link Toolbar Toolbar's} showMenuItemsWhileSearching is true.
+         *
+         * <p>If using this, you should only change the id, visibility, or onClickListener.
          */
-        public static MenuItem createSearch(Context c, OnClickListener listener) {
-            return new Builder(c)
-                    .setTitle(R.string.car_ui_toolbar_menu_item_search_title)
-                    .setIcon(R.drawable.car_ui_icon_search)
-                    .setOnClickListener(listener)
-                    .setSearch()
-                    .build();
+        public Builder setToSearch() {
+            mIsSearch = true;
+            setTitle(mSearchTitle);
+            setIcon(mSearchIcon);
+            return this;
         }
 
         /**
@@ -528,13 +549,32 @@
          * <p>The advantage of this over creating your own is getting an OEM-styled settings icon,
          * and that the MenuItem will be restricted based on
          * {@link CarUxRestrictions#UX_RESTRICTIONS_NO_SETUP}
+         *
+         * <p>If using this, you should only change the id, visibility, or onClickListener.
          */
-        public static MenuItem createSettings(Context c, OnClickListener listener) {
-            return new Builder(c)
-                    .setTitle(R.string.car_ui_toolbar_menu_item_settings_title)
-                    .setIcon(R.drawable.car_ui_icon_settings)
+        public Builder setToSettings() {
+            mIsSettings = true;
+            setTitle(mSettingsTitle);
+            setIcon(mSettingsIcon);
+            setUxRestrictions(CarUxRestrictions.UX_RESTRICTIONS_NO_SETUP);
+            return this;
+        }
+
+        /** @deprecated Use {@link #setToSearch()} instead. */
+        @Deprecated
+        public static MenuItem createSearch(Context c, OnClickListener listener) {
+            return MenuItem.builder(c)
+                    .setToSearch()
                     .setOnClickListener(listener)
-                    .setUxRestrictions(CarUxRestrictions.UX_RESTRICTIONS_NO_SETUP)
+                    .build();
+        }
+
+        /** @deprecated Use {@link #setToSettings()} instead. */
+        @Deprecated
+        public static MenuItem createSettings(Context c, OnClickListener listener) {
+            return MenuItem.builder(c)
+                    .setToSettings()
+                    .setOnClickListener(listener)
                     .build();
         }
     }
diff --git a/car-ui-lib/src/com/android/car/ui/toolbar/MenuItemRenderer.java b/car-ui-lib/src/com/android/car/ui/toolbar/MenuItemRenderer.java
index a0f5eb5..4254bdf 100644
--- a/car-ui-lib/src/com/android/car/ui/toolbar/MenuItemRenderer.java
+++ b/car-ui-lib/src/com/android/car/ui/toolbar/MenuItemRenderer.java
@@ -213,6 +213,8 @@
             int id = a.getResourceId(R.styleable.CarUiToolbarMenuItem_id, View.NO_ID);
             String title = a.getString(R.styleable.CarUiToolbarMenuItem_title);
             Drawable icon = a.getDrawable(R.styleable.CarUiToolbarMenuItem_icon);
+            boolean isSearch = a.getBoolean(R.styleable.CarUiToolbarMenuItem_search, false);
+            boolean isSettings = a.getBoolean(R.styleable.CarUiToolbarMenuItem_settings, false);
             boolean tinted = a.getBoolean(R.styleable.CarUiToolbarMenuItem_tinted, true);
             boolean visible = a.getBoolean(R.styleable.CarUiToolbarMenuItem_visible, true);
             boolean showIconAndTitle = a.getBoolean(
@@ -267,6 +269,14 @@
                     .setShowIconAndTitle(showIconAndTitle)
                     .setDisplayBehavior(displayBehavior);
 
+            if (isSearch) {
+                builder.setToSearch();
+            }
+
+            if (isSettings) {
+                builder.setToSettings();
+            }
+
             if (checkable || checkedExists) {
                 builder.setChecked(checked);
             }
diff --git a/car-ui-lib/src/com/android/car/ui/toolbar/Toolbar.java b/car-ui-lib/src/com/android/car/ui/toolbar/Toolbar.java
index 02ef2b7..c8899fb 100644
--- a/car-ui-lib/src/com/android/car/ui/toolbar/Toolbar.java
+++ b/car-ui-lib/src/com/android/car/ui/toolbar/Toolbar.java
@@ -721,8 +721,7 @@
     /**
      * Set whether or not to show the {@link MenuItem MenuItems} while searching. Default false.
      * Even if this is set to true, the {@link MenuItem} created by
-     * {@link MenuItem.Builder#createSearch(Context, MenuItem.OnClickListener)} will still be
-     * hidden.
+     * {@link MenuItem.Builder#setToSearch()} will still be hidden.
      */
     public void setShowMenuItemsWhileSearching(boolean showMenuItems) {
         mShowMenuItemsWhileSearching = showMenuItems;
diff --git a/car-ui-lib/tests/paintbooth/res/xml/menuitems.xml b/car-ui-lib/tests/paintbooth/res/xml/menuitems.xml
index 7899572..4f22379 100644
--- a/car-ui-lib/tests/paintbooth/res/xml/menuitems.xml
+++ b/car-ui-lib/tests/paintbooth/res/xml/menuitems.xml
@@ -15,6 +15,8 @@
   ~ limitations under the License.
   -->
 <MenuItems xmlns:app="http://schemas.android.com/apk/res-auto">
+    <MenuItem app:search="true"/>
+    <MenuItem app:settings="true"/>
     <MenuItem
         app:title="@string/preferences_screen_title"/>
     <MenuItem
diff --git a/car-ui-lib/tests/paintbooth/src/com/android/car/ui/paintbooth/toolbar/ToolbarActivity.java b/car-ui-lib/tests/paintbooth/src/com/android/car/ui/paintbooth/toolbar/ToolbarActivity.java
index d9178b9..a5e7fa3 100644
--- a/car-ui-lib/tests/paintbooth/src/com/android/car/ui/paintbooth/toolbar/ToolbarActivity.java
+++ b/car-ui-lib/tests/paintbooth/src/com/android/car/ui/paintbooth/toolbar/ToolbarActivity.java
@@ -62,8 +62,10 @@
                     return false;
                 });
 
-        mMenuItems.add(
-                MenuItem.Builder.createSearch(this, i -> toolbar.setState(Toolbar.State.SEARCH)));
+        mMenuItems.add(MenuItem.builder(this)
+                .setToSearch()
+                .setOnClickListener(i -> toolbar.setState(Toolbar.State.SEARCH))
+                .build());
 
         toolbar.setMenuItems(mMenuItems);
 
@@ -86,9 +88,11 @@
         }));
 
         mButtons.add(Pair.create(getString(R.string.toolbar_add_icon), v -> {
-            mMenuItems.add(MenuItem.Builder.createSettings(
-                    this, i -> Toast.makeText(this, "Clicked",
-                            Toast.LENGTH_SHORT).show()));
+            mMenuItems.add(MenuItem.builder(this)
+                    .setToSettings()
+                    .setOnClickListener(i -> Toast.makeText(this, "Clicked",
+                            Toast.LENGTH_SHORT).show())
+                    .build());
             toolbar.setMenuItems(mMenuItems);
         }));
 
diff --git a/car-ui-lib/tests/robotests/src/com/android/car/ui/toolbar/ToolbarTest.java b/car-ui-lib/tests/robotests/src/com/android/car/ui/toolbar/ToolbarTest.java
index 239caee..1744d76 100644
--- a/car-ui-lib/tests/robotests/src/com/android/car/ui/toolbar/ToolbarTest.java
+++ b/car-ui-lib/tests/robotests/src/com/android/car/ui/toolbar/ToolbarTest.java
@@ -417,8 +417,7 @@
     @Test
     public void menuItems_searchScreen_shouldHideMenuItems() {
         mToolbar.setMenuItems(Arrays.asList(
-                MenuItem.Builder.createSearch(mContext, i -> {
-                }),
+                MenuItem.builder(mContext).setToSearch().build(),
                 createMenuItem(i -> {
                 })));
 
@@ -432,8 +431,7 @@
     @Test
     public void menuItems_showMenuItemsWhileSearching() {
         mToolbar.setMenuItems(Arrays.asList(
-                MenuItem.Builder.createSearch(mContext, i -> {
-                }),
+                MenuItem.builder(mContext).setToSearch().build(),
                 createMenuItem(i -> {
                 })));
 
@@ -445,7 +443,7 @@
     }
 
     private MenuItem createMenuItem(MenuItem.OnClickListener listener) {
-        return new MenuItem.Builder(mContext)
+        return MenuItem.builder(mContext)
                 .setTitle("Button!")
                 .setOnClickListener(listener)
                 .build();