DO NOT MERGE - Merge QD1A.190821.011 into stage-aosp-master

Bug: 141248619
Change-Id: Ia88438d2da134b522b41e6b5545ba01e4e7b6e4d
diff --git a/car-apps-common/res/values-night/colors.xml b/car-apps-common/res/values-night/colors.xml
index 7625452..6a2f4cf 100644
--- a/car-apps-common/res/values-night/colors.xml
+++ b/car-apps-common/res/values-night/colors.xml
@@ -22,7 +22,7 @@
     <color name="minimized_control_bar_background_color">#E00E1013</color>
 
     <color name="primary_text_color">#E0FFFFFF</color>
-    <color name="secondary_text_color">#B7FFFFFF</color>
+    <color name="secondary_text_color">#99FFFFFF</color>
 
     <color name="primary_app_icon_color">#E0FFFFFF</color>
     <color name="secondary_app_icon_color">#99FFFFFF</color>
diff --git a/car-apps-common/res/values/colors.xml b/car-apps-common/res/values/colors.xml
index 3ba5069..3104f1d 100644
--- a/car-apps-common/res/values/colors.xml
+++ b/car-apps-common/res/values/colors.xml
@@ -50,7 +50,7 @@
     <color name="car_tab_unselected_color_light">#90FFFFFF</color>
 
     <color name="primary_text_color">#FFFFFFFF</color>
-    <color name="secondary_text_color">#90FFFFFF</color>
+    <color name="secondary_text_color">#B8FFFFFF</color>
 
     <color name="primary_app_icon_color">#FFFFFFFF</color>
     <color name="secondary_app_icon_color">#B8FFFFFF</color>
diff --git a/car-chassis-lib/res/color/chassis_tab_item_selector.xml b/car-chassis-lib/res/color/chassis_tab_item_selector.xml
new file mode 100644
index 0000000..22f747f
--- /dev/null
+++ b/car-chassis-lib/res/color/chassis_tab_item_selector.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2019 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.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@color/chassis_tab_selected_color" android:state_selected="true"/>
+    <item android:color="@color/chassis_tab_unselected_color"/>
+</selector>
diff --git a/car-chassis-lib/res/drawable/chassis_toolbar_button_background.xml b/car-chassis-lib/res/drawable/chassis_toolbar_button_background.xml
new file mode 100644
index 0000000..7d29d64
--- /dev/null
+++ b/car-chassis-lib/res/drawable/chassis_toolbar_button_background.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~
+  ~ Copyright (C) 2019 Google Inc.
+  ~
+  ~ 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.
+  ~
+ -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+        android:color="@*android:color/car_card_ripple_background"
+        android:radius="@dimen/chassis_toolbar_button_background_radius"/>
diff --git a/car-chassis-lib/res/drawable/ic_arrow_back.xml b/car-chassis-lib/res/drawable/ic_arrow_back.xml
new file mode 100644
index 0000000..fcbba7c
--- /dev/null
+++ b/car-chassis-lib/res/drawable/ic_arrow_back.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2018, 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
+</vector>
diff --git a/car-chassis-lib/res/drawable/ic_close.xml b/car-chassis-lib/res/drawable/ic_close.xml
new file mode 100644
index 0000000..48ab552
--- /dev/null
+++ b/car-chassis-lib/res/drawable/ic_close.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright 2019 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.
+-->
+
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="@dimen/primary_icon_size"
+    android:height="@dimen/primary_icon_size"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0">
+    <path
+        android:fillColor="#FFF"
+        android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
+</vector>
\ No newline at end of file
diff --git a/car-chassis-lib/res/drawable/ic_search.xml b/car-chassis-lib/res/drawable/ic_search.xml
new file mode 100644
index 0000000..87e7d46
--- /dev/null
+++ b/car-chassis-lib/res/drawable/ic_search.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="@dimen/primary_icon_size"
+    android:height="@dimen/primary_icon_size"
+    android:viewportWidth="48"
+    android:viewportHeight="48">
+
+    <path
+        android:fillColor="@*android:color/car_grey_50"
+        android:pathData="M31 28h-1.59l-.55-.55C30.82 25.18 32 22.23 32 19c0-7.18-5.82-13-13-13S6 11.82 6
+19s5.82 13 13 13c3.23 0 6.18-1.18 8.45-3.13l.55 .55 V31l10 9.98L40.98 38 31
+28zm-12 0c-4.97 0-9-4.03-9-9s4.03-9 9-9 9 4.03 9 9-4.03 9-9 9z" />
+    <path
+        android:pathData="M0 0h48v48H0z" />
+</vector>
diff --git a/car-chassis-lib/res/drawable/ic_settings.xml b/car-chassis-lib/res/drawable/ic_settings.xml
new file mode 100644
index 0000000..5c9e6a7
--- /dev/null
+++ b/car-chassis-lib/res/drawable/ic_settings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="@dimen/primary_icon_size"
+        android:height="@dimen/primary_icon_size"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+
+    <path
+        android:pathData="M0 0h20v20H0V0z" />
+    <path
+        android:fillColor="#fffafafa"
+        android:pathData="M21.4 14.2l-1.94-1.45c.03-.25 .04 -.5 .04 -.76s-.01-.51-.04-.76L21.4 9.8c.42-.31
+.52 -.94 .24 -1.41l-1.6-2.76c-.28-.48-.88-.7-1.36-.5l-2.14 .91
+c-.48-.37-1.01-.68-1.57-.92l-.27-2.2c-.06-.52-.56-.92-1.11-.92h-3.18c-.55 0-1.05
+.4 -1.11 .92 l-.26 2.19c-.57 .24 -1.1 .55 -1.58 .92 l-2.14-.91c-.48-.2-1.08 .02
+-1.36 .5 l-1.6 2.76c-.28 .48 -.18 1.1 .24 1.42l1.94 1.45c-.03 .24 -.04 .49 -.04
+.75 s.01 .51 .04 .76 L2.6 14.2c-.42 .31 -.52 .94 -.24 1.41l1.6 2.76c.28 .48 .88
+.7 1.36 .5 l2.14-.91c.48 .37 1.01 .68 1.57 .92 l.27 2.19c.06 .53 .56 .93 1.11
+.93 h3.18c.55 0 1.04-.4 1.11-.92l.27-2.19c.56-.24 1.09-.55 1.57-.92l2.14 .91
+c.48 .2 1.08-.02 1.36-.5l1.6-2.76c.28-.48 .18 -1.1-.24-1.42zM12 15.5c-1.93
+0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z"/>
+</vector>
diff --git a/car-chassis-lib/res/layout-port/chassis_toolbar.xml b/car-chassis-lib/res/layout-port/chassis_toolbar.xml
new file mode 100644
index 0000000..48cacb3
--- /dev/null
+++ b/car-chassis-lib/res/layout-port/chassis_toolbar.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2019, 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.
+-->
+<androidx.constraintlayout.widget.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    style="@style/ChassisToolbarContainerStyle">
+
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/row_separator_guideline"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:orientation="horizontal"
+        app:layout_constraintGuide_begin="@dimen/chassis_toolbar_first_row_height"/>
+
+    <View
+        android:id="@+id/row_separator"
+        style="@style/ChassisToolbarRowSeparatorStyle"
+        app:layout_constraintTop_toBottomOf="@id/row_separator_guideline"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"/>
+
+    <FrameLayout
+        android:id="@+id/nav_icon_container"
+        style="@style/ChassisToolbarNavIconContainerStyle"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/row_separator"
+        app:layout_constraintStart_toStartOf="parent">
+        <ImageView
+            android:id="@+id/nav_icon"
+            style="@style/ChassisToolbarNavIconStyle"/>
+        <ImageView
+            android:id="@+id/logo"
+            style="@style/ChassisToolbarLogoStyle"/>
+    </FrameLayout>
+
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        style="@style/ChassisToolbarTitleStyle"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/row_separator"
+        app:layout_constraintStart_toEndOf="@id/nav_icon_container"
+        app:layout_constraintEnd_toStartOf="@id/buttons_container"/>
+
+    <com.android.car.chassis.SearchView
+        android:id="@+id/search_view"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:visibility="gone"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/row_separator"
+        app:layout_constraintStart_toEndOf="@id/nav_icon_container"
+        app:layout_constraintEnd_toStartOf="@id/buttons_container"/>
+
+    <FrameLayout
+        android:id="@+id/custom_view_container"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:visibility="gone"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/row_separator"
+        app:layout_constraintStart_toEndOf="@id/nav_icon_container"
+        app:layout_constraintEnd_toStartOf="@id/buttons_container"/>
+
+    <LinearLayout
+        android:id="@+id/buttons_container"
+        android:layout_width="wrap_content"
+        android:layout_height="0dp"
+        android:orientation="horizontal"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/row_separator"
+        app:layout_constraintEnd_toEndOf="parent"/>
+
+    <com.android.car.chassis.TabLayout
+        android:id="@+id/tabs"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/chassis_toolbar_second_row_height"
+        app:layout_constraintTop_toBottomOf="@id/row_separator"
+        app:layout_constraintBottom_toTopOf="@id/bottom_styleable"/>
+
+    <View
+        android:id="@+id/bottom_styleable"
+        style="@style/ChassisToolbarBottomStyle"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"/>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/car-chassis-lib/res/layout/chassis_search_view.xml b/car-chassis-lib/res/layout/chassis_search_view.xml
new file mode 100644
index 0000000..e9942a7
--- /dev/null
+++ b/car-chassis-lib/res/layout/chassis_search_view.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 2019 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.
+  -->
+
+<merge
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <ImageView
+        android:id="@+id/icon"
+        android:layout_width="@dimen/touch_target_size"
+        android:layout_height="@dimen/touch_target_size"
+        android:src="@drawable/ic_search"
+        android:scaleType="center"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"/>
+
+    <EditText
+        android:id="@+id/search_bar"
+        android:layout_height="match_parent"
+        android:layout_width="match_parent"
+        android:paddingLeft="@dimen/touch_target_size"
+        android:hint="@string/chassis_default_search_hint"
+        android:textColorHint="@color/search_hint_text_color"
+        android:inputType="text"
+        android:singleLine="true"
+        android:imeOptions="actionDone"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"/>
+
+    <ImageView
+        android:id="@+id/search_close"
+        android:layout_width="@dimen/touch_target_size"
+        android:layout_height="@dimen/touch_target_size"
+        android:background="@drawable/chassis_toolbar_button_background"
+        android:src="@drawable/ic_close"
+        android:scaleType="center"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"/>
+</merge>
diff --git a/car-chassis-lib/res/layout/chassis_tab_item.xml b/car-chassis-lib/res/layout/chassis_tab_item.xml
new file mode 100644
index 0000000..dd2c97f
--- /dev/null
+++ b/car-chassis-lib/res/layout/chassis_tab_item.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2019 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.
+  -->
+
+<merge
+    xmlns:android="http://schemas.android.com/apk/res/android">
+    <ImageView
+        android:id="@+id/chassis_tab_item_icon"
+        style="@style/ChassisTabItemIcon"/>
+    <TextView
+        android:id="@+id/chassis_tab_item_text"
+        style="@style/ChassisTabItemText"/>
+</merge>
diff --git a/car-chassis-lib/res/layout/chassis_toolbar.xml b/car-chassis-lib/res/layout/chassis_toolbar.xml
new file mode 100644
index 0000000..56559f7
--- /dev/null
+++ b/car-chassis-lib/res/layout/chassis_toolbar.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2019, 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.
+-->
+<androidx.constraintlayout.widget.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/chassis_toolbar_first_row_height"
+    style="@style/ChassisToolbarContainerStyle">
+
+    <FrameLayout
+        android:id="@+id/nav_icon_container"
+        style="@style/ChassisToolbarNavIconContainerStyle"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/bottom_styleable"
+        app:layout_constraintStart_toStartOf="parent">
+        <ImageView
+            android:id="@+id/nav_icon"
+            style="@style/ChassisToolbarNavIconStyle"/>
+        <ImageView
+            android:id="@+id/logo"
+            style="@style/ChassisToolbarLogoStyle"/>
+    </FrameLayout>
+
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        style="@style/ChassisToolbarTitleStyle"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/bottom_styleable"
+        app:layout_constraintStart_toEndOf="@id/nav_icon_container"
+        app:layout_constraintEnd_toStartOf="@id/buttons_container"/>
+
+    <com.android.car.chassis.TabLayout
+        android:id="@+id/tabs"
+        android:layout_width="wrap_content"
+        android:layout_height="0dp"
+        android:layout_marginEnd="@dimen/chassis_toolbar_custom_button_margin"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/bottom_styleable"
+        app:layout_constraintStart_toEndOf="@id/nav_icon_container"
+        app:layout_constraintEnd_toStartOf="@id/buttons_container"
+        app:layout_constraintHorizontal_bias="0.0"
+        app:layout_constrainedWidth="true"/>
+
+    <FrameLayout
+        android:id="@+id/custom_view_container"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:visibility="gone"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/bottom_styleable"
+        app:layout_constraintStart_toEndOf="@id/nav_icon_container"
+        app:layout_constraintEnd_toStartOf="@id/buttons_container"/>
+
+    <LinearLayout
+        android:id="@+id/buttons_container"
+        android:layout_width="wrap_content"
+        android:layout_height="0dp"
+        android:orientation="horizontal"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/bottom_styleable"
+        app:layout_constraintEnd_toEndOf="parent"/>
+
+    <com.android.car.chassis.SearchView
+        android:id="@+id/search_view"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:visibility="gone"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/bottom_styleable"
+        app:layout_constraintStart_toEndOf="@id/nav_icon_container"
+        app:layout_constraintEnd_toStartOf="@id/buttons_container"/>
+
+    <View
+        android:id="@+id/bottom_styleable"
+        style="@style/ChassisToolbarBottomStyle"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"/>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/car-chassis-lib/res/layout/chassis_toolbar_search_button.xml b/car-chassis-lib/res/layout/chassis_toolbar_search_button.xml
new file mode 100644
index 0000000..91afe95
--- /dev/null
+++ b/car-chassis-lib/res/layout/chassis_toolbar_search_button.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2019, 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:id="@+id/search"
+    android:layout_width="@dimen/touch_target_size"
+    android:layout_height="@dimen/touch_target_size"
+    android:src="@drawable/ic_search"
+    android:scaleType="center"
+    android:background="@drawable/chassis_toolbar_button_background"/>
diff --git a/car-chassis-lib/res/layout/chassis_toolbar_settings_button.xml b/car-chassis-lib/res/layout/chassis_toolbar_settings_button.xml
new file mode 100644
index 0000000..c295d5c
--- /dev/null
+++ b/car-chassis-lib/res/layout/chassis_toolbar_settings_button.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2019, 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:id="@+id/settings"
+    android:layout_width="@dimen/touch_target_size"
+    android:layout_height="@dimen/touch_target_size"
+    android:src="@drawable/ic_settings"
+    android:scaleType="center"
+    android:background="@drawable/chassis_toolbar_button_background"/>
diff --git a/car-chassis-lib/res/values-night/colors.xml b/car-chassis-lib/res/values-night/colors.xml
new file mode 100644
index 0000000..0ee4613
--- /dev/null
+++ b/car-chassis-lib/res/values-night/colors.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+<resources>
+    <color name="chassis_tab_selected_color">@color/chassis_tab_selected_color_dark</color>
+    <color name="chassis_tab_unselected_color">@color/chassis_tab_unselected_color_dark</color>
+</resources>
diff --git a/car-chassis-lib/res/values-port/dimens.xml b/car-chassis-lib/res/values-port/dimens.xml
new file mode 100644
index 0000000..d91a195
--- /dev/null
+++ b/car-chassis-lib/res/values-port/dimens.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2019 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.
+  -->
+<resources>
+    <dimen name="chassis_toolbar_second_row_height">@*android:dimen/car_app_bar_height</dimen>
+</resources>
diff --git a/car-chassis-lib/res/values-port/values.xml b/car-chassis-lib/res/values-port/values.xml
new file mode 100644
index 0000000..39590dd
--- /dev/null
+++ b/car-chassis-lib/res/values-port/values.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2019 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.
+  -->
+
+<resources>
+    <bool name="chassis_tab_flexible_layout">true</bool>
+</resources>
diff --git a/car-chassis-lib/res/values/attrs.xml b/car-chassis-lib/res/values/attrs.xml
index b946e88..5a49a20 100644
--- a/car-chassis-lib/res/values/attrs.xml
+++ b/car-chassis-lib/res/values/attrs.xml
@@ -14,10 +14,19 @@
  limitations under the License.
 -->
 <resources>
-    <!-- Attributes for the ClickThroughToolbar. -->
-    <declare-styleable name="ClickThroughToolbar">
-        <!-- Whether or not clicks on this toolbar will pass through to an underlying view. This
-             value is false by default. -->
-        <attr name="clickThrough" format="boolean"/>
+    <declare-styleable name="ChassisToolbar">
+        <!-- The title of the toolbar, only displayed in certain conditions -->
+        <attr name="title" format="string"/>
+        <!-- The logo drawable for the toolbar. Appears when there's no back/close button shown -->
+        <attr name="logo" format="reference"/>
+        <!-- The hint for the search bar in the toolbar -->
+        <attr name="searchHint" format="string"/>
+        <!-- The buttons to display, as an array of layout ids. Use @layout/chassis_toolbar_search_button and @layout/chassis_toolbar_settings_button for the search and settings buttons -->
+        <attr name="buttons" format="reference"/>
+        <!-- Whether or not to show the custom buttons while searching. If using chassis_toolbar_search_button, or any other layout with a view with the id of "search", it will always be hidden. -->
+        <attr name="showButtonsWhileSearching" format="boolean"/>
     </declare-styleable>
+
+    <!-- Theme attribute to specifying a default style for all chassisToolbars -->
+    <attr name="chassisToolbarStyle" format="reference"/>
 </resources>
diff --git a/car-chassis-lib/res/values/colors.xml b/car-chassis-lib/res/values/colors.xml
new file mode 100644
index 0000000..7c06fa2
--- /dev/null
+++ b/car-chassis-lib/res/values/colors.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+<resources>
+    <color name="primary_text_color">#FFFFFFFF</color>
+    <color name="secondary_text_color">#90FFFFFF</color>
+    <color name="search_hint_text_color">#33FFFFFF</color>
+
+    <color name="toolbar_background_color">#E0000000</color>
+
+    <color name="chassis_tab_selected_color">@color/chassis_tab_selected_color_light</color>
+    <color name="chassis_tab_selected_color_dark">#E2FFFFFF</color>
+    <color name="chassis_tab_selected_color_light">#FFFFFFFF</color>
+    <color name="chassis_tab_unselected_color">@color/chassis_tab_unselected_color_light</color>
+    <color name="chassis_tab_unselected_color_dark">#80FFFFFF</color>
+    <color name="chassis_tab_unselected_color_light">#90FFFFFF</color>
+</resources>
diff --git a/car-chassis-lib/res/values/dimens.xml b/car-chassis-lib/res/values/dimens.xml
new file mode 100644
index 0000000..6bc61dd
--- /dev/null
+++ b/car-chassis-lib/res/values/dimens.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+<resources>
+    <dimen name="touch_target_size">@*android:dimen/car_touch_target_size</dimen>
+    <dimen name="primary_icon_size">@*android:dimen/car_primary_icon_size</dimen>
+    <item name="letter_spacing_body1" format="float" type="dimen">0.0</item>
+    <item name="letter_spacing_body3" format="float" type="dimen">0.0</item>
+
+    <!-- Tabs -->
+    <dimen name="chassis_tab_width">135dp</dimen>
+    <dimen name="chassis_tab_padding_x">12dp</dimen>
+    <dimen name="chassis_tab_icon_size">36dp</dimen>
+
+    <!-- Car toolbar -->
+    <dimen name="chassis_toolbar_view_nav_button_width">@*android:dimen/car_margin</dimen>
+    <dimen name="chassis_toolbar_first_row_height">@*android:dimen/car_app_bar_height</dimen>
+    <dimen name="chassis_toolbar_second_row_height">0dp</dimen>
+    <dimen name="chassis_toolbar_view_icon_size">@*android:dimen/car_primary_icon_size</dimen>
+    <dimen name="chassis_toolbar_view_title_margin_start">@*android:dimen/car_padding_2</dimen>
+    <dimen name="chassis_toolbar_custom_button_margin">@*android:dimen/car_padding_2</dimen>
+    <dimen name="chassis_toolbar_button_background_radius">48dp</dimen>
+</resources>
diff --git a/car-chassis-lib/res/values/strings.xml b/car-chassis-lib/res/values/strings.xml
new file mode 100644
index 0000000..dd6770a
--- /dev/null
+++ b/car-chassis-lib/res/values/strings.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+<resources>
+    <string name="chassis_default_search_hint">Search&#8230;</string>
+</resources>
diff --git a/car-chassis-lib/res/values/styles.xml b/car-chassis-lib/res/values/styles.xml
new file mode 100644
index 0000000..5214d2d
--- /dev/null
+++ b/car-chassis-lib/res/values/styles.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <!-- Styles for chassis tab view -->
+    <style name="ChassisTabItemText">
+        <item name="android:textAppearance">@style/TextAppearance.Body3</item>
+        <item name="android:textColor">@color/chassis_tab_item_selector</item>
+        <item name="android:layout_width">@dimen/chassis_tab_width</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:singleLine">true</item>
+        <item name="android:gravity">center</item>
+    </style>
+
+    <style name="ChassisTabItemIcon">
+        <item name="android:layout_width">@dimen/chassis_tab_icon_size</item>
+        <item name="android:layout_height">@dimen/chassis_tab_icon_size</item>
+        <item name="android:scaleType">fitCenter</item>
+        <item name="android:tint">@color/chassis_tab_item_selector</item>
+        <item name="android:tintMode">src_in</item>
+    </style>
+
+    <style name="ChassisTabItemBackground">
+        <item name="android:background">?android:attr/selectableItemBackground</item>
+    </style>
+
+    <style name="ChassisToolbarTitleStyle">
+        <item name="android:textAppearance">@style/TextAppearance.Body1</item>
+        <item name="android:singleLine">true</item>
+        <item name="android:layout_marginStart">@dimen/chassis_toolbar_view_title_margin_start</item>
+    </style>
+
+    <style name="ChassisToolbarContainerStyle"/>
+
+    <style name="ChassisToolbarRowSeparatorStyle">
+        <item name="android:layout_width">match_parent</item>
+        <!-- can't use 0dp or the constraintlayout effect kicks in -->
+        <item name="android:layout_height">0.01dp</item>
+    </style>
+
+    <style name="ChassisToolbarBottomStyle">
+        <item name="android:layout_width">match_parent</item>
+        <!-- can't use 0dp or the constraintlayout effect kicks in -->
+        <item name="android:layout_height">0.01dp</item>
+    </style>
+
+    <style name="ChassisToolbarNavIconContainerStyle">
+        <item name="android:layout_width">@dimen/chassis_toolbar_view_nav_button_width</item>
+        <item name="android:layout_height">0dp</item>
+        <item name="android:background">?android:attr/selectableItemBackground</item>
+    </style>
+
+    <style name="ChassisToolbarNavIconStyle">
+        <item name="android:layout_width">@dimen/chassis_toolbar_view_icon_size</item>
+        <item name="android:layout_height">@dimen/chassis_toolbar_view_icon_size</item>
+        <item name="android:layout_gravity">center</item>
+        <item name="android:src">@drawable/ic_arrow_back</item>
+        <item name="android:scaleType">fitXY</item>
+    </style>
+
+    <style name="ChassisToolbarLogoStyle">
+        <item name="android:layout_width">@dimen/chassis_toolbar_view_icon_size</item>
+        <item name="android:layout_height">@dimen/chassis_toolbar_view_icon_size</item>
+        <item name="android:layout_gravity">center</item>
+        <item name="android:scaleType">fitXY</item>
+    </style>
+
+    <style name="TextAppearance">
+        <item name="android:fontFamily">roboto-regular</item>
+        <item name="android:textColor">@color/primary_text_color</item>
+    </style>
+
+    <style name="TextAppearance.Body1" parent="TextAppearance">
+        <item name="android:textSize">32sp</item>
+        <item name="android:letterSpacing">@dimen/letter_spacing_body1</item>
+    </style>
+
+    <style name="TextAppearance.Body3" parent="TextAppearance">
+        <item name="android:textSize">24sp</item>
+        <item name="android:letterSpacing">@dimen/letter_spacing_body3</item>
+    </style>
+
+</resources>
diff --git a/car-chassis-lib/res/values/values.xml b/car-chassis-lib/res/values/values.xml
new file mode 100644
index 0000000..ee9b403
--- /dev/null
+++ b/car-chassis-lib/res/values/values.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2019 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.
+  -->
+
+<resources>
+    <bool name="chassis_tab_flexible_layout">false</bool>
+    <item name="chassis_tab_item_layout" type="layout">@layout/chassis_tab_item</item>
+</resources>
diff --git a/car-chassis-lib/src/com/android/car/chassis/ClickThroughToolbar.java b/car-chassis-lib/src/com/android/car/chassis/ClickThroughToolbar.java
deleted file mode 100644
index e0c1120..0000000
--- a/car-chassis-lib/src/com/android/car/chassis/ClickThroughToolbar.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2019 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 com.android.car.chassis;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.widget.Toolbar;
-
-/**
- * A toolbar that optionally supports allowing clicks on it to pass through to any underlying views.
- *
- * <p>By default, the {@link Toolbar} eats all touches on it. This view will override
- * {@link #onTouchEvent(MotionEvent)} and return {@code false} if configured to allow pass through.
- */
-public class ClickThroughToolbar extends Toolbar {
-    private boolean mAllowClickPassThrough;
-
-    public ClickThroughToolbar(Context context) {
-        super(context);
-    }
-
-    public ClickThroughToolbar(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        initAttributes(context, attrs, 0 /* defStyleAttrs */);
-    }
-
-    public ClickThroughToolbar(Context context, AttributeSet attrs, int defStyleAttrs) {
-        super(context, attrs, defStyleAttrs);
-        initAttributes(context, attrs, defStyleAttrs);
-    }
-
-    private void initAttributes(Context context, AttributeSet attrs, int defStyleAttrs) {
-        TypedArray a = context.obtainStyledAttributes(
-                attrs, R.styleable.ClickThroughToolbar, defStyleAttrs, 0 /* defStyleRes */);
-
-        mAllowClickPassThrough = a.getBoolean(R.styleable.ClickThroughToolbar_clickThrough, false);
-
-        a.recycle();
-    }
-
-    /**
-     * Whether or not clicks on this toolbar will pass through to any views that are underneath
-     * it. By default, this value is {@code false}.
-     *
-     * @param allowPassThrough {@code true} if clicks will pass through to an underlying view;
-     *                         {@code false} otherwise.
-     */
-    public void setClickPassThrough(boolean allowPassThrough) {
-        mAllowClickPassThrough = allowPassThrough;
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        if (mAllowClickPassThrough) {
-            return false;
-        }
-
-        return super.onTouchEvent(ev);
-    }
-}
diff --git a/car-chassis-lib/src/com/android/car/chassis/SearchView.java b/car-chassis-lib/src/com/android/car/chassis/SearchView.java
new file mode 100644
index 0000000..c033f7e
--- /dev/null
+++ b/car-chassis-lib/src/com/android/car/chassis/SearchView.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2019 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 com.android.car.chassis;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+import android.widget.ImageView;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A search view used by {@link Toolbar}.
+ */
+public class SearchView extends ConstraintLayout {
+    private final ImageView mIcon;
+    private final EditText mSearchText;
+
+    private Set<Toolbar.Listener> mListeners = new HashSet<>();
+
+    public SearchView(Context context) {
+        this(context, null);
+    }
+
+    public SearchView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public SearchView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        LayoutInflater inflater = LayoutInflater.from(context);
+        inflater.inflate(R.layout.chassis_search_view, this, true);
+
+        mSearchText = requireViewById(R.id.search_bar);
+        View closeIcon = requireViewById(R.id.search_close);
+        closeIcon.setOnClickListener(view -> mSearchText.getText().clear());
+        mIcon = requireViewById(R.id.icon);
+
+        mSearchText.setOnFocusChangeListener(
+                (view, hasFocus) -> {
+                    if (hasFocus) {
+                        ((InputMethodManager)
+                                context.getSystemService(Context.INPUT_METHOD_SERVICE))
+                                .showSoftInput(view, 0);
+                    } else {
+                        ((InputMethodManager)
+                                context.getSystemService(Context.INPUT_METHOD_SERVICE))
+                                .hideSoftInputFromWindow(view.getWindowToken(), 0);
+                    }
+                });
+        mSearchText.addTextChangedListener(new TextWatcher() {
+            @Override
+            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
+            }
+
+            @Override
+            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
+            }
+
+            @Override
+            public void afterTextChanged(Editable editable) {
+                onSearch(editable.toString());
+            }
+        });
+
+        mSearchText.setOnEditorActionListener((v, actionId, event) -> {
+            if (actionId == EditorInfo.IME_ACTION_DONE
+                    || actionId == EditorInfo.IME_ACTION_SEARCH) {
+                mSearchText.clearFocus();
+            }
+            return false;
+        });
+    }
+
+    /**
+     * Adds a listener for the search text changing.
+     * See also {@link #removeToolbarListener(Toolbar.Listener)}
+     */
+    public void addToolbarListener(Toolbar.Listener listener) {
+        mListeners.add(listener);
+    }
+
+    /**
+     * Removes a listener.
+     * See also {@link #addToolbarListener(Toolbar.Listener)}
+     * @param listener
+     */
+    public void removeToolbarListener(Toolbar.Listener listener) {
+        mListeners.remove(listener);
+    }
+
+    /**
+     * Sets the search hint.
+     * @param resId A string resource id of the search hint.
+     */
+    public void setHint(int resId) {
+        mSearchText.setHint(resId);
+    }
+
+    /**
+     * Sets the search hint
+     * @param hint A CharSequence of the search hint.
+     */
+    public void setHint(CharSequence hint) {
+        mSearchText.setHint(hint);
+    }
+
+    /**
+     * Sets a custom icon to display in the search box.
+     */
+    public void setIcon(Bitmap b) {
+        mIcon.setImageBitmap(b);
+    }
+
+    /**
+     * Sets a custom icon to display in the search box.
+     */
+    public void setIcon(Drawable d) {
+        mIcon.setImageDrawable(d);
+    }
+
+    /**
+     * Sets a custom icon to display in the search box.
+     */
+    public void setIcon(int resId) {
+        mIcon.setImageResource(resId);
+    }
+
+    private void onSearch(String query) {
+        for (Toolbar.Listener listener : mListeners) {
+            listener.onSearch(query);
+        }
+    }
+
+    /**
+     * Sets the text being searched.
+     */
+    public void setSearchQuery(String query) {
+        mSearchText.setText(query);
+        mSearchText.setSelection(mSearchText.getText().length());
+    }
+}
diff --git a/car-chassis-lib/src/com/android/car/chassis/TabLayout.java b/car-chassis-lib/src/com/android/car/chassis/TabLayout.java
new file mode 100644
index 0000000..d9803e4
--- /dev/null
+++ b/car-chassis-lib/src/com/android/car/chassis/TabLayout.java
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2019 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 com.android.car.chassis;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.util.ArraySet;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.LayoutRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Custom tab layout which supports adding tabs dynamically
+ *
+ * <p>It supports two layout modes:
+ * <ul><li>Flexible layout which will fill the width
+ * <li>Non-flexible layout which wraps content with a minimum tab width. By setting tab gravity,
+ * it can left aligned, right aligned or center aligned.
+ *
+ * <p>Scrolling function is not supported. If a tab item runs out of the tab layout bound, there
+ * is no way to access it. It's better to set the layout mode to flexible in this case.
+ *
+ * <p>Default tab item inflates from R.layout.chassis_tab_item, but it also supports custom layout
+ * id, by overlaying R.layout.chassis_tab_item_layout. By doing this, appearance of tab item view
+ * can be customized.
+ *
+ * <p>Touch feedback is using @android:attr/selectableItemBackground.
+ *
+ */
+public class TabLayout extends LinearLayout {
+
+    /**
+     * Listener that listens the tab selection change.
+     */
+    public interface Listener {
+        /** Callback triggered when a tab is selected. */
+        default void onTabSelected(Tab tab) {}
+
+        /** Callback triggered when a tab is unselected. */
+        default void onTabUnselected(Tab tab) {}
+
+        /** Callback triggered when a tab is reselected. */
+        default void onTabReselected(Tab tab) {}
+    }
+
+    // View attributes
+    private final boolean mTabFlexibleLayout;
+    private final int mTabPaddingX;
+
+    private final Set<Listener> mListeners;
+
+    private final TabAdapter mTabAdapter;
+
+    public TabLayout(@NonNull Context context) {
+        this(context, null);
+    }
+
+    public TabLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public TabLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        mListeners = new ArraySet<>();
+
+        mTabPaddingX = context.getResources().getDimensionPixelSize(R.dimen.chassis_tab_padding_x);
+        mTabFlexibleLayout = context.getResources().getBoolean(R.bool.chassis_tab_flexible_layout);
+
+        mTabAdapter = new TabAdapter(context, R.layout.chassis_tab_item_layout, this);
+    }
+
+    /**
+     * Add a tab to this layout. The tab will be added at the end of the list. If this is the first
+     * tab to be added it will become the selected tab.
+     */
+    public void addTab(Tab tab) {
+        mTabAdapter.add(tab);
+        // If there is only one tab in the group, set it to be selected.
+        if (mTabAdapter.getCount() == 1) {
+            mTabAdapter.selectTab(0);
+        }
+    }
+
+    /** Set the tab as the current selected tab. */
+    public void selectTab(Tab tab) {
+        mTabAdapter.selectTab(tab);
+    }
+
+    /** Set the tab at given position as the current selected tab. */
+    public void selectTab(int position) {
+        mTabAdapter.selectTab(position);
+    }
+
+    /** Returns how tab items it has. */
+    public int getTabCount() {
+        return mTabAdapter.getCount();
+    }
+
+    /** Returns the position of the given tab. */
+    public int getTabPosition(Tab tab) {
+        return mTabAdapter.getPosition(tab);
+    }
+
+    /** Return the tab at the given position. */
+    public Tab get(int position) {
+        return mTabAdapter.getItem(position);
+    }
+
+    /** Clear all tabs. */
+    public void clearAllTabs() {
+        mTabAdapter.clear();
+    }
+
+    /** Register a {@link Listener}. Same listener will only be registered once. */
+    public void addListener(@NonNull Listener listener) {
+        mListeners.add(listener);
+    }
+
+    /** Unregister a {@link Listener} */
+    public void removeListener(@NonNull Listener listener) {
+        mListeners.remove(listener);
+    }
+
+    private void dispatchOnTabSelected(Tab tab) {
+        for (Listener listener : mListeners) {
+            listener.onTabSelected(tab);
+        }
+    }
+
+    private void dispatchOnTabUnselected(Tab tab) {
+        for (Listener listener : mListeners) {
+            listener.onTabUnselected(tab);
+        }
+    }
+
+    private void dispatchOnTabReselected(Tab tab) {
+        for (Listener listener : mListeners) {
+            listener.onTabReselected(tab);
+        }
+    }
+
+    private void addTabView(View tabView, int position) {
+        LayoutParams layoutParams;
+        if (mTabFlexibleLayout) {
+            layoutParams = new LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT);
+            layoutParams.weight = 1;
+        } else {
+            layoutParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+                    ViewGroup.LayoutParams.MATCH_PARENT);
+        }
+        addView(tabView, position, layoutParams);
+    }
+
+    private ViewGroup createTabItemView() {
+        LinearLayout tabItemView = new LinearLayout(mContext);
+        tabItemView.setOrientation(LinearLayout.VERTICAL);
+        tabItemView.setGravity(Gravity.CENTER);
+        tabItemView.setPadding(mTabPaddingX, 0, mTabPaddingX, 0);
+        TypedArray ta = getContext().obtainStyledAttributes(R.style.ChassisTabItemBackground,
+                new int[]{android.R.attr.background});
+        Drawable backgroundDrawable = ta.getDrawable(0);
+        ta.recycle();
+        tabItemView.setBackground(backgroundDrawable);
+        return tabItemView;
+    }
+
+    private static class TabAdapter extends BaseAdapter {
+        private static final int MEDIUM_WEIGHT = 500;
+        private final Context mContext;
+        private final TabLayout mTabLayout;
+        @LayoutRes
+        private final int mTabItemLayoutRes;
+        private final Typeface mUnselectedTypeface;
+        private final Typeface mSelectedTypeface;
+        private final List<Tab> mTabList;
+
+        private TabAdapter(Context context, @LayoutRes int res, TabLayout tabLayout) {
+            mTabList = new ArrayList<>();
+            mContext = context;
+            mTabItemLayoutRes = res;
+            mTabLayout = tabLayout;
+            mUnselectedTypeface = Typeface.defaultFromStyle(Typeface.NORMAL);
+            // TODO: add indirection to allow customization.
+            mSelectedTypeface = Typeface.create(mUnselectedTypeface, MEDIUM_WEIGHT, false);
+        }
+
+        private void add(@NonNull Tab tab) {
+            mTabList.add(tab);
+            notifyItemInserted(mTabList.size() - 1);
+        }
+
+        private void clear() {
+            mTabList.clear();
+            mTabLayout.removeAllViews();
+        }
+
+        private int getPosition(Tab tab) {
+            return mTabList.indexOf(tab);
+        }
+
+        @Override
+        public int getCount() {
+            return mTabList.size();
+        }
+
+        @Override
+        public Tab getItem(int position) {
+            return mTabList.get(position);
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
+
+        @Override
+        @NonNull
+        public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
+            ViewGroup tabItemView = mTabLayout.createTabItemView();
+            LayoutInflater.from(mContext).inflate(mTabItemLayoutRes, tabItemView, true);
+
+            presentTabItemView(position, tabItemView);
+            return tabItemView;
+        }
+
+        private void selectTab(Tab tab) {
+            selectTab(getPosition(tab));
+        }
+
+        private void selectTab(int position) {
+            if (position < 0 || position >= getCount()) {
+                throw new IndexOutOfBoundsException("Invalid position");
+            }
+
+            for (int i = 0; i < getCount(); i++) {
+                Tab tab = mTabList.get(i);
+                boolean isTabSelected = position == i;
+                if (tab.mIsSelected != isTabSelected) {
+                    tab.mIsSelected = isTabSelected;
+                    notifyItemChanged(i);
+                    if (tab.mIsSelected) {
+                        mTabLayout.dispatchOnTabSelected(tab);
+                    } else {
+                        mTabLayout.dispatchOnTabUnselected(tab);
+                    }
+                } else if (tab.mIsSelected) {
+                    mTabLayout.dispatchOnTabReselected(tab);
+                }
+            }
+        }
+
+        /** Represent the tab item at given position without destroying and recreating UI. */
+        private void notifyItemChanged(int position) {
+            View tabItemView = mTabLayout.getChildAt(position);
+            presentTabItemView(position, tabItemView);
+        }
+
+        private void notifyItemInserted(int position) {
+            View insertedView = getView(position, null, mTabLayout);
+            mTabLayout.addTabView(insertedView, position);
+        }
+
+        private void presentTabItemView(int position, @NonNull View tabItemView) {
+            Tab tab = mTabList.get(position);
+
+            ImageView iconView = tabItemView.findViewById(R.id.chassis_tab_item_icon);
+            TextView textView = tabItemView.findViewById(R.id.chassis_tab_item_text);
+
+            tabItemView.setOnClickListener(view -> selectTab(tab));
+            tab.bindText(textView);
+            tab.bindIcon(iconView);
+
+            tabItemView.setSelected(tab.mIsSelected);
+            iconView.setSelected(tab.mIsSelected);
+            textView.setSelected(tab.mIsSelected);
+            textView.setTypeface(tab.mIsSelected ? mSelectedTypeface : mUnselectedTypeface);
+        }
+    }
+
+    /** Tab entity. */
+    public static class Tab {
+        private final Drawable mIcon;
+        private final CharSequence mText;
+        private boolean mIsSelected;
+
+        public Tab(@Nullable Drawable icon, @Nullable CharSequence text) {
+            mIcon = icon;
+            mText = text;
+        }
+
+        /** Set tab text. */
+        protected void bindText(TextView textView) {
+            textView.setText(mText);
+        }
+
+        /** Set icon drawable. */
+        protected void bindIcon(ImageView imageView) {
+            imageView.setImageDrawable(mIcon);
+        }
+    }
+}
diff --git a/car-chassis-lib/src/com/android/car/chassis/Toolbar.java b/car-chassis-lib/src/com/android/car/chassis/Toolbar.java
new file mode 100644
index 0000000..47ab0c2
--- /dev/null
+++ b/car-chassis-lib/src/com/android/car/chassis/Toolbar.java
@@ -0,0 +1,406 @@
+/*
+ * Copyright (C) 2019 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 com.android.car.chassis;
+
+import android.annotation.StringRes;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Consumer;
+
+/**
+ * A toolbar for Android Automotive OS apps.
+ *
+ * This isn't a toolbar in the android framework sense, it's merely a custom view that can be
+ * added to a layout. (You can't call
+ * {@link android.app.Activity#setActionBar(android.widget.Toolbar)} with it)
+ *
+ * The toolbar supports a navigation button, title, tabs, search, and custom buttons.
+ */
+public class Toolbar extends FrameLayout {
+
+    /** Enum of states the toolbar can be in. Controls what elements of the toolbar are displayed */
+    public enum State {
+        /**
+         * In the HOME state, the logo will be displayed if there is one, and no navigation icon
+         * will be displayed. The tab bar will be visible. The title will be displayed if there
+         * is space. Custom buttons will be displayed.
+         */
+        HOME,
+        /**
+         * In the SUBPAGE state, the logo will be replaced with a back button, the tab bar won't
+         * be visible. The title and custom buttons will be displayed.
+         */
+        SUBPAGE,
+        /**
+         * In the SUBPAGE_CUSTOM state, everything is the same as SUBPAGE except the title will
+         * be hidden and the custom view will be shown.
+         */
+        SUBPAGE_CUSTOM,
+        /**
+         * In the SEARCH state, only the back button and the search bar will be visible.
+         */
+        SEARCH,
+    }
+
+    private ImageView mNavIcon;
+    private ImageView mLogo;
+    private ViewGroup mNavIconContainer;
+    private TextView mTitle;
+    private TabLayout mTabLayout;
+    private LinearLayout mButtonsContainer;
+    private FrameLayout mCustomViewContainer;
+    private Set<Listener> mListeners = new HashSet<>();
+    private SearchView mSearchView;
+    private boolean mHasLogo = false;
+    private int[] mCurrentButtons;
+    private boolean mShowButtonsWhileSearching;
+    private View mSearchButton;
+    private State mState = State.HOME;
+
+    public Toolbar(Context context) {
+        this(context, null);
+    }
+
+    public Toolbar(Context context, AttributeSet attrs) {
+        this(context, attrs, R.attr.chassisToolbarStyle);
+    }
+
+    public Toolbar(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public Toolbar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+
+        LayoutInflater inflater = (LayoutInflater) context
+                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        inflater.inflate(R.layout.chassis_toolbar, this, true);
+
+        mTabLayout = requireViewById(R.id.tabs);
+        mNavIcon = requireViewById(R.id.nav_icon);
+        mLogo = requireViewById(R.id.logo);
+        mNavIconContainer = requireViewById(R.id.nav_icon_container);
+        mButtonsContainer = requireViewById(R.id.buttons_container);
+        mTitle = requireViewById(R.id.title);
+        mSearchView = requireViewById(R.id.search_view);
+        mCustomViewContainer = requireViewById(R.id.custom_view_container);
+
+        TypedArray a = context.obtainStyledAttributes(
+                attrs, R.styleable.ChassisToolbar, defStyleAttr, defStyleRes);
+
+        mTitle.setText(a.getString(R.styleable.ChassisToolbar_title));
+        setLogo(a.getResourceId(R.styleable.ChassisToolbar_logo, 0));
+        setButtons(a.getResourceId(R.styleable.ChassisToolbar_buttons, 0));
+        mShowButtonsWhileSearching = a.getBoolean(
+                R.styleable.ChassisToolbar_showButtonsWhileSearching, false);
+        String searchHint = a.getString(R.styleable.ChassisToolbar_searchHint);
+        if (searchHint != null) {
+            setSearchHint(searchHint);
+        }
+
+        a.recycle();
+
+        // If an android:background attribute wasn't given, set the default one
+        TypedArray viewAttributes = context.obtainStyledAttributes(
+                attrs, com.android.internal.R.styleable.View, defStyleAttr, defStyleRes);
+        if (viewAttributes.getDrawable(com.android.internal.R.styleable.View_background) == null) {
+            setBackground(context.getDrawable(R.color.toolbar_background_color));
+        }
+
+        mTabLayout.addListener(new TabLayout.Listener() {
+            @Override
+            public void onTabSelected(TabLayout.Tab tab) {
+                forEachListener(listener -> listener.onTabSelected(tab));
+            }
+        });
+    }
+
+    /**
+     * Sets the title of the toolbar to a string resource.
+     *
+     * The title may not always be shown, for example in landscape with tabs.
+     */
+    public void setTitle(@StringRes int title) {
+        mTitle.setText(title);
+    }
+
+    /**
+     * Sets the title of the toolbar to a CharSequence.
+     *
+     * The title may not always be shown, for example in landscape with tabs.
+     */
+    public void setTitle(CharSequence title) {
+        mTitle.setText(title);
+    }
+
+    /**
+     * Gets the {@link TabLayout} for this toolbar.
+     */
+    public TabLayout getTabLayout() {
+        return mTabLayout;
+    }
+
+    /**
+     * Adds a tab to this toolbar. You can listen for when it is selected via
+     * {@link #addListener(Listener)}.
+     */
+    public void addTab(TabLayout.Tab tab) {
+        mTabLayout.addTab(tab);
+    }
+
+    /**
+     * Gets a tab added to this toolbar. See
+     * {@link #addTab(TabLayout.Tab)}.
+     */
+    public TabLayout.Tab getTab(int position) {
+        return mTabLayout.get(position);
+    }
+
+    /**
+     * Selects a tab added to this toolbar. See
+     * {@link #addTab(TabLayout.Tab)}.
+     */
+    public void selectTab(int position) {
+        mTabLayout.selectTab(position);
+    }
+
+    /**
+     * Sets the logo to display in this toolbar.
+     * Will not be displayed if a navigation icon is currently being displayed.
+     */
+    public void setLogo(int resId) {
+        if (resId != 0) {
+            mLogo.setImageResource(resId);
+            mHasLogo = true;
+        } else {
+            mHasLogo = false;
+        }
+        setState(mState);
+    }
+
+    /**
+     * Sets the hint for the search bar.
+     */
+    public void setSearchHint(int resId) {
+        mSearchView.setHint(resId);
+    }
+
+    /**
+     * Sets the hint for the search bar.
+     */
+    public void setSearchHint(CharSequence hint) {
+        mSearchView.setHint(hint);
+    }
+
+    /**
+     * Sets the buttons to be shown. Click events for these buttons will be received in
+     * {@link Listener#onCustomButtonPressed(View)}.
+     *
+     * Buttons are encouraged to use @drawable/chassis_toolbar_button_background as their
+     * background. In the default implementation it's a ripple that is sized appropriately to fit
+     * the toolbar.
+     *
+     * R.layout.chassis_toolbar_search_button can be used to add a search button, which will have an
+     * id of R.id.search in {@link Listener#onCustomButtonPressed(View)} callback.
+     *
+     * R.layout.chassis_toolbar_settings_button can be used to add a search button, which will have
+     * an id of R.id.settings in {@link Listener#onCustomButtonPressed(View)} callback.
+     *
+     * @param buttons An array of layout ids specifying the buttons to show. Toolbar will keep
+     *                a reference to this array, so don't modify it afterwards.
+     */
+    public void setButtons(@Nullable int[] buttons) {
+        if (!Arrays.equals(buttons, mCurrentButtons)) {
+            mButtonsContainer.removeAllViews();
+
+            if (buttons != null) {
+                LayoutInflater inflater = (LayoutInflater) getContext()
+                        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+                for (int button : buttons) {
+                    View v = inflater.inflate(button, mButtonsContainer, false);
+                    mButtonsContainer.addView(v);
+
+                    setupCustomButton(v);
+                }
+            }
+
+            mSearchButton = mButtonsContainer.findViewById(R.id.search);
+            mCurrentButtons = buttons;
+            setState(mState);
+        }
+    }
+
+    private void setButtons(TypedArray buttons) {
+        int[] layouts = new int[buttons.length()];
+        for (int i = 0; i < buttons.length(); ++i) {
+            layouts[i] = buttons.getResourceId(i, 0);
+        }
+        setButtons(layouts);
+    }
+
+    /**
+     * Sets the buttons to be shown, based on an XML array of layouts. See
+     * {@link #setButtons(int[])} for more info.
+     *
+     * @param arrayId A resource id of an array of layouts.
+     */
+    public void setButtons(int arrayId) {
+        if (arrayId == 0) {
+            mButtonsContainer.removeAllViews();
+            mCurrentButtons = null;
+        } else {
+            setButtons(getContext().getResources().obtainTypedArray(arrayId));
+        }
+    }
+
+    private void setupCustomButton(View v) {
+        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) v.getLayoutParams();
+        lp.rightMargin = getContext().getResources().getDimensionPixelSize(
+                R.dimen.chassis_toolbar_custom_button_margin);
+        lp.leftMargin = lp.rightMargin;
+        lp.gravity = Gravity.CENTER_VERTICAL;
+
+        v.setOnClickListener(x -> forEachListener(listener -> listener.onCustomButtonPressed(x)));
+    }
+
+    /**
+     * Set whether or not to show the custom buttons while searching. Default false.
+     * Even if this is set to true, if there is a button with the id "search", such as the button
+     * added by R.layout.chassis_toolbar_search_button, that buttons will always be hidden.
+     */
+    public void setShowButtonsWhileSearching(boolean showButtons) {
+        mShowButtonsWhileSearching = showButtons;
+        setState(mState);
+    }
+
+    /**
+     * Sets the search query.
+     */
+    public void setSearchQuery(String query) {
+        mSearchView.setSearchQuery(query);
+    }
+
+    /**
+     * Sets a custom view to display, and sets the current state to {@link State#SUBPAGE_CUSTOM}.
+     *
+     * @param resId A layout id of the view to display.
+     * @return The inflated custom view.
+     */
+    public View setCustomView(int resId) {
+        mCustomViewContainer.removeAllViews();
+        LayoutInflater inflater = (LayoutInflater) getContext()
+                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        View v = inflater.inflate(resId, mCustomViewContainer, false);
+        mCustomViewContainer.addView(v);
+        setState(State.SUBPAGE_CUSTOM);
+        return v;
+    }
+
+    /**
+     * Sets the state of the toolbar. This will show/hide the appropriate elements of the toolbar
+     * for the desired state.
+     */
+    public void setState(State state) {
+        mState = state;
+
+        View.OnClickListener backClickListener = (v) -> forEachListener(Listener::onBack);
+        mNavIcon.setVisibility(state != State.HOME ? VISIBLE : INVISIBLE);
+        mNavIcon.setImageResource(state != State.HOME ? R.drawable.ic_arrow_back : 0);
+        mLogo.setVisibility(state == State.HOME && mHasLogo ? VISIBLE : INVISIBLE);
+        mNavIconContainer.setVisibility(state != State.HOME || mHasLogo ? VISIBLE : GONE);
+        mNavIconContainer.setClickable(state != State.HOME);
+        mNavIconContainer.setOnClickListener(state != State.HOME ? backClickListener : null);
+        mTitle.setVisibility(state == State.HOME || state == State.SUBPAGE ? VISIBLE : GONE);
+        mTabLayout.setVisibility(state == State.HOME ? VISIBLE : GONE);
+        mSearchView.setVisibility(state == State.SEARCH ? VISIBLE : GONE);
+        mButtonsContainer.setVisibility(state != State.SEARCH || mShowButtonsWhileSearching
+                ? VISIBLE : GONE);
+        if (mSearchButton != null) {
+            mSearchButton.setVisibility(state != State.SEARCH ? VISIBLE : GONE);
+        }
+        mCustomViewContainer.setVisibility(state == State.SUBPAGE_CUSTOM ? VISIBLE : GONE);
+        if (state != State.SUBPAGE_CUSTOM) {
+            mCustomViewContainer.removeAllViews();
+        }
+    }
+
+    /**
+     * Toolbar listener.
+     */
+    public interface Listener {
+        /**
+         * Invoked when the user selects an item from the tabs
+         */
+        default void onTabSelected(TabLayout.Tab item) {}
+
+        /**
+         * Invoked when the user clicks on the back button
+         */
+        default void onBack() {}
+
+        /**
+         * Invoked when the user submits a search query.
+         */
+        default void onSearch(String query) {}
+
+        /**
+         * Invoked when the user clicks on a custom button
+         * @param v The button that was clicked
+         */
+        default void onCustomButtonPressed(View v) {}
+    }
+
+    /**
+     * Adds a {@link Listener} to this toolbar.
+     */
+    public void addListener(Listener listener) {
+        mListeners.add(listener);
+        mSearchView.addToolbarListener(listener);
+    }
+
+    /**
+     * Removes a {@link Listener} from this toolbar.
+     */
+    public boolean removeListener(Listener listener) {
+        mSearchView.removeToolbarListener(listener);
+        return mListeners.remove(listener);
+    }
+
+    private void forEachListener(Consumer<Listener> callback) {
+        List<Listener> listenersCopy = new ArrayList<>(mListeners);
+        for (Listener listener : listenersCopy) {
+            callback.accept(listener);
+        }
+    }
+}