Merge "Add drawer activities." into oc-mr1-jetpack-dev
diff --git a/car/Android.mk b/car/Android.mk
index a4d1f0d..fa20f26 100644
--- a/car/Android.mk
+++ b/car/Android.mk
@@ -34,6 +34,6 @@
android-support-v7-cardview \
android-support-v7-recyclerview
LOCAL_JAR_EXCLUDE_FILES := none
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+LOCAL_JAVA_LANGUAGE_VERSION := 1.8
LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/car/build.gradle b/car/build.gradle
index d489b3d..5a55b56 100644
--- a/car/build.gradle
+++ b/car/build.gradle
@@ -28,4 +28,5 @@
publish false
inceptionYear '2017'
description 'Android Car Support UI'
+ java8Library true
}
diff --git a/car/res/drawable-hdpi/ic_list_view_disable.png b/car/res/drawable-hdpi/ic_list_view_disable.png
new file mode 100644
index 0000000..e82a74f
--- /dev/null
+++ b/car/res/drawable-hdpi/ic_list_view_disable.png
Binary files differ
diff --git a/car/res/drawable-mdpi/ic_list_view_disable.png b/car/res/drawable-mdpi/ic_list_view_disable.png
new file mode 100644
index 0000000..9887c8e
--- /dev/null
+++ b/car/res/drawable-mdpi/ic_list_view_disable.png
Binary files differ
diff --git a/car/res/drawable-xhdpi/ic_list_view_disable.png b/car/res/drawable-xhdpi/ic_list_view_disable.png
new file mode 100644
index 0000000..32edc30
--- /dev/null
+++ b/car/res/drawable-xhdpi/ic_list_view_disable.png
Binary files differ
diff --git a/car/res/drawable-xxhdpi/ic_list_view_disable.png b/car/res/drawable-xxhdpi/ic_list_view_disable.png
new file mode 100644
index 0000000..1f61690
--- /dev/null
+++ b/car/res/drawable-xxhdpi/ic_list_view_disable.png
Binary files differ
diff --git a/car/res/drawable/car_drawer_list_item_background.xml b/car/res/drawable/car_drawer_list_item_background.xml
new file mode 100644
index 0000000..c5fc36b
--- /dev/null
+++ b/car/res/drawable/car_drawer_list_item_background.xml
@@ -0,0 +1,22 @@
+<?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.
+ -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/car_card_ripple_background">
+ <item android:id="@android:id/mask">
+ <color android:color="#ffffffff" />
+ </item>
+</ripple>
diff --git a/car/res/layout/car_drawer.xml b/car/res/layout/car_drawer.xml
new file mode 100644
index 0000000..a3b5c4f
--- /dev/null
+++ b/car/res/layout/car_drawer.xml
@@ -0,0 +1,40 @@
+<?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.
+-->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/drawer_content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginEnd="@dimen/car_drawer_margin_end"
+ android:background="@color/car_card"
+ android:paddingTop="@dimen/app_header_height" >
+
+ <android.support.car.widget.PagedListView
+ android:id="@+id/drawer_list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:listEndMargin="@dimen/car_drawer_margin_end"
+ app:offsetScrollBar="true" />
+
+ <ProgressBar
+ android:id="@+id/drawer_progress"
+ android:layout_width="@dimen/car_drawer_progress_bar_size"
+ android:layout_height="@dimen/car_drawer_progress_bar_size"
+ android:layout_gravity="center"
+ android:indeterminate="true"
+ android:visibility="gone" />
+</FrameLayout>
diff --git a/car/res/layout/car_drawer_activity.xml b/car/res/layout/car_drawer_activity.xml
new file mode 100644
index 0000000..751ef0d
--- /dev/null
+++ b/car/res/layout/car_drawer_activity.xml
@@ -0,0 +1,40 @@
+<?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.
+-->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <android.support.v4.widget.DrawerLayout
+ android:id="@+id/drawer_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <!-- The main content view. Fragments will be added here. -->
+ <FrameLayout
+ android:id="@+id/content_frame"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <include
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="start"
+ layout="@layout/car_drawer" />
+ </android.support.v4.widget.DrawerLayout>
+
+ <include layout="@layout/car_toolbar" />
+</FrameLayout>
diff --git a/car/res/layout/car_drawer_list_item_empty.xml b/car/res/layout/car_drawer_list_item_empty.xml
new file mode 100644
index 0000000..c2e35ac
--- /dev/null
+++ b/car/res/layout/car_drawer_list_item_empty.xml
@@ -0,0 +1,45 @@
+<?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.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginStart="16dp"
+ android:focusable="false"
+ android:orientation="vertical"
+ android:background="@drawable/car_drawer_list_item_background" >
+ <FrameLayout
+ android:id="@+id/icon_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="visible">
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginTop="48dp"
+ android:layout_marginBottom="22dp" />
+ </FrameLayout>
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="16dp"
+ android:gravity="center"
+ style="@style/CarBody1" />
+</LinearLayout>
diff --git a/car/res/layout/car_drawer_list_item_normal.xml b/car/res/layout/car_drawer_list_item_normal.xml
new file mode 100644
index 0000000..fa3b29f
--- /dev/null
+++ b/car/res/layout/car_drawer_list_item_normal.xml
@@ -0,0 +1,59 @@
+<?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.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/car_drawer_list_item_height"
+ android:focusable="true"
+ android:orientation="horizontal"
+ android:background="@drawable/car_drawer_list_item_background" >
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="@dimen/car_drawer_list_item_icon_size"
+ android:layout_height="@dimen/car_drawer_list_item_icon_size"
+ android:layout_marginEnd="@dimen/car_drawer_list_item_icon_end_margin"
+ android:layout_gravity="center_vertical"
+ android:scaleType="centerCrop" />
+ <LinearLayout
+ android:id="@+id/text_container"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="center_vertical"
+ android:orientation="vertical" >
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/car_text_vertical_margin"
+ android:maxLines="1"
+ style="@style/CarBody1" />
+ <TextView
+ android:id="@+id/text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="1"
+ style="@style/CarBody2" />
+ </LinearLayout>
+ <ImageView
+ android:id="@+id/end_icon"
+ android:layout_width="@dimen/car_drawer_list_item_end_icon_size"
+ android:layout_height="@dimen/car_drawer_list_item_end_icon_size"
+ android:scaleType="fitCenter"
+ android:layout_marginEnd="@dimen/car_drawer_list_item_end_margin"
+ android:layout_gravity="center_vertical" />
+</LinearLayout>
diff --git a/car/res/layout/car_drawer_list_item_small.xml b/car/res/layout/car_drawer_list_item_small.xml
new file mode 100644
index 0000000..d7e7a75
--- /dev/null
+++ b/car/res/layout/car_drawer_list_item_small.xml
@@ -0,0 +1,46 @@
+<?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.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/car_drawer_list_item_height_small"
+ android:focusable="true"
+ android:orientation="horizontal"
+ android:background="@drawable/car_drawer_list_item_background" >
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="@dimen/car_drawer_list_item_small_icon_size"
+ android:layout_height="@dimen/car_drawer_list_item_small_icon_size"
+ android:layout_marginEnd="@dimen/car_drawer_list_item_icon_end_margin"
+ android:layout_gravity="center_vertical"
+ android:scaleType="centerCrop" />
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="center_vertical"
+ android:layout_marginBottom="@dimen/car_text_vertical_margin"
+ android:maxLines="1"
+ style="@style/CarBody1" />
+ <ImageView
+ android:id="@+id/end_icon"
+ android:layout_width="@dimen/car_drawer_list_item_end_icon_size"
+ android:layout_height="@dimen/car_drawer_list_item_end_icon_size"
+ android:scaleType="fitCenter"
+ android:layout_marginEnd="@dimen/car_drawer_list_item_end_margin"
+ android:layout_gravity="center_vertical"/>
+</LinearLayout>
diff --git a/car/res/layout/car_paged_recycler_view.xml b/car/res/layout/car_paged_recycler_view.xml
index 68e6ae7..30129df 100644
--- a/car/res/layout/car_paged_recycler_view.xml
+++ b/car/res/layout/car_paged_recycler_view.xml
@@ -26,19 +26,11 @@
android:paddingTop="@dimen/car_scroll_bar_padding"
android:visibility="invisible" />
- <FrameLayout
- android:id="@+id/recycler_view_container"
+ <android.support.car.widget.CarRecyclerView
+ android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:layout_gravity="center_horizontal"
android:layout_marginStart="@dimen/car_paged_list_view_pagination_width"
- android:clipChildren="false">
-
- <android.support.car.widget.CarRecyclerView
- android:id="@+id/recycler_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center_horizontal"
- android:clipChildren="false" />
- </FrameLayout>
-
-</merge>
\ No newline at end of file
+ android:clipChildren="false" />
+</merge>
diff --git a/car/res/layout/car_toolbar.xml b/car/res/layout/car_toolbar.xml
new file mode 100644
index 0000000..fb7769a
--- /dev/null
+++ b/car/res/layout/car_toolbar.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/app_header_height">
+ <android.support.v7.widget.Toolbar
+ android:id="@+id/car_toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ style="@style/CarToolbarTheme" />
+</FrameLayout>
diff --git a/car/res/values-h480dp/dimens.xml b/car/res/values-h480dp/dimens.xml
new file mode 100644
index 0000000..8dd16fc
--- /dev/null
+++ b/car/res/values-h480dp/dimens.xml
@@ -0,0 +1,18 @@
+<?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.
+-->
+<resources>
+ <dimen name="app_header_height">112dp</dimen>
+</resources>
diff --git a/car/res/values-h600dp/dimens.xml b/car/res/values-h600dp/dimens.xml
new file mode 100644
index 0000000..7577d17
--- /dev/null
+++ b/car/res/values-h600dp/dimens.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.
+-->
+<resources>
+ <dimen name="car_title_size">32sp</dimen>
+ <dimen name="car_body1_size">40sp</dimen>
+ <dimen name="car_body2_size">32sp</dimen>
+
+ <dimen name="app_header_height">148dp</dimen>
+
+ <dimen name="car_drawer_list_item_icon_size">108dp</dimen>
+ <dimen name="car_drawer_list_item_small_icon_size">56dp</dimen>
+ <dimen name="car_drawer_list_item_end_icon_size">56dp</dimen>
+
+ <dimen name="car_drawer_list_item_height">128dp</dimen>
+ <dimen name="car_drawer_list_item_height_small">128dp</dimen>
+</resources>
diff --git a/car/res/values-night/colors.xml b/car/res/values-night/colors.xml
index 970cc49..2ca5b02 100644
--- a/car/res/values-night/colors.xml
+++ b/car/res/values-night/colors.xml
@@ -15,11 +15,18 @@
~ limitations under the License.
-->
<resources>
+ <color name="car_title">@color/car_title_light</color>
+ <color name="car_body1">@color/car_body1_light</color>
+ <color name="car_body2">@color/car_body2_light</color>
+
<color name="car_tint">@color/car_tint_light</color>
<color name="car_tint_inverse">@color/car_tint_dark</color>
+
+ <color name="car_card">@color/car_card_dark</color>
<color name="car_card_ripple_background">@color/car_card_ripple_background_light</color>
<color name="car_card_ripple_background_inverse">@color/car_card_ripple_background_dark</color>
+
<color name="car_list_divider">@color/car_list_divider_dark</color>
<color name="car_scrollbar_thumb">@color/car_scrollbar_thumb_light</color>
<color name="car_scrollbar_thumb_inverse">@color/car_scrollbar_thumb_dark</color>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/car/res/values-w1920dp/dimens.xml b/car/res/values-w1920dp/dimens.xml
new file mode 100644
index 0000000..decab1a
--- /dev/null
+++ b/car/res/values-w1920dp/dimens.xml
@@ -0,0 +1,18 @@
+<?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.
+-->
+<resources>
+ <dimen name="car_keyline_1">48dp</dimen>
+</resources>
diff --git a/car/res/values-w840dp/dimens.xml b/car/res/values-w840dp/dimens.xml
index fad31ed..6428ce0 100644
--- a/car/res/values-w840dp/dimens.xml
+++ b/car/res/values-w840dp/dimens.xml
@@ -14,6 +14,7 @@
limitations under the License.
-->
<resources>
+ <dimen name="car_keyline_1">32dp</dimen>
<dimen name="car_screen_margin_size">40dp</dimen>
<dimen name="car_screen_gutter_size">24dp</dimen>
</resources>
diff --git a/car/res/values/attrs.xml b/car/res/values/attrs.xml
index c21e45e..17a1960 100644
--- a/car/res/values/attrs.xml
+++ b/car/res/values/attrs.xml
@@ -27,9 +27,9 @@
<attr name="fadeLastItem" format="boolean" />
<!-- Set to true/false to offset rows as they slide off screen. Defaults to true -->
<attr name="offsetRows" format="boolean" />
- <!-- Whether or not there is a margin on the right that matches the width of the container
- for the scrollbars. -->
- <attr name="rightGutterEnabled" format="boolean" />
+ <!-- Whether or not to offset the list view by the width of scroll bar. Setting this to
+ true will ensure that any views within the list will not overlap the scroll bar. -->
+ <attr name="offsetScrollBar" format="boolean" />
<!-- Whether to display the scrollbar or not. Defaults to true. -->
<attr name="scrollBarEnabled" format="boolean" />
<!-- Whether or not to show a diving line between each item of the list. -->
@@ -43,5 +43,28 @@
<!-- A starting margin before the drawing of the dividing line. This margin will be an
offset from the view specified by "alignDividerStartTo" if given. -->
<attr name="dividerStartMargin" format="dimension" />
+ <!-- The width of the margin on the right side of the list -->
+ <attr name="listEndMargin" format="dimension" />
+ </declare-styleable>
+
+ <!-- The attributes for customizing the appearance of the hamburger and back arrow in the
+ drawer. -->
+ <declare-styleable name="DrawerArrowDrawable">
+ <!-- The color of the arrow. -->
+ <attr name="carArrowColor" format="color"/>
+ <!-- Whether the arrow will animate when switches directions. -->
+ <attr name="carArrowAnimate" format="boolean"/>
+ <!-- The size of the arrow's bounding box. -->
+ <attr name="carArrowSize" format="dimension"/>
+ <!-- The length of the top and bottom bars that merge to form the point of the arrow. -->
+ <attr name="carArrowHeadLength" format="dimension"/>
+ <!-- The length of arrow shaft. -->
+ <attr name="carArrowShaftLength" format="dimension"/>
+ <!-- The thickness of each of the bars that form the arrow. -->
+ <attr name="carArrowThickness" format="dimension"/>
+ <!-- The spacing between the menu bars (i.e. the "hamburger" icon). -->
+ <attr name="carMenuBarSpacing" format="dimension"/>
+ <!-- The size of the menu bars (i.e. the "hamburger" icon). -->
+ <attr name="carMenuBarThickness" format="dimension"/>
</declare-styleable>
</resources>
diff --git a/car/res/values/colors.xml b/car/res/values/colors.xml
index ddb3dd3..1ae98af 100644
--- a/car/res/values/colors.xml
+++ b/car/res/values/colors.xml
@@ -18,8 +18,28 @@
<!-- These colors are from
http://www.google.com/design/spec/style/color.html#color-ui-color-palette -->
<color name="car_grey_50">#fffafafa</color>
+ <color name="car_grey_100">#fff5f5f5</color>
+ <color name="car_grey_500">#ff9e9e9e</color>
<color name="car_grey_900">#ff212121</color>
+ <!-- Car specific colors that are not from the color palette. -->
+ <color name="car_grey_650">#ff6B6B6B</color>
+ <color name="car_darkbluegrey_700">#ff172026</color>
+
+ <!-- Various colors for text sizes. "Light" and "dark" here refer to the lighter or darker
+ shades. -->
+ <color name="car_title_light">@color/car_grey_100</color>
+ <color name="car_title_dark">@color/car_grey_900</color>
+ <color name="car_title">@color/car_title_dark</color>
+
+ <color name="car_body1_light">@color/car_grey_100</color>
+ <color name="car_body1_dark">@color/car_grey_900</color>
+ <color name="car_body1">@color/car_body1_dark</color>
+
+ <color name="car_body2_light">@color/car_grey_500</color>
+ <color name="car_body2_dark">@color/car_grey_650</color>
+ <color name="car_body2">@color/car_body2_dark</color>
+
<!-- The tinting colors to create a light- and dark-colored icon respectively. -->
<color name="car_tint_light">@color/car_grey_50</color>
<color name="car_tint_dark">@color/car_grey_900</color>
@@ -40,6 +60,13 @@
view. -->
<color name="car_list_divider">@color/car_list_divider_dark</color>
+ <!-- A light and dark colored card. -->
+ <color name="car_card_light">@color/car_grey_50</color>
+ <color name="car_card_dark">@color/car_darkbluegrey_700</color>
+
+ <!-- The default color of a card in car UI. -->
+ <color name="car_card">@color/car_card_light</color>
+
<!-- The ripple colors. The "dark" and "light" designation here refers to the color of the
ripple itself. -->
<color name="car_card_ripple_background_dark">#17000000</color>
@@ -52,6 +79,9 @@
car_card_ripple_background. -->
<color name="car_card_ripple_background_inverse">@color/car_card_ripple_background_light</color>
+ <!-- The top margin before the start of content in an application. -->
+ <dimen name="app_header_height">96dp</dimen>
+
<!-- The lighter and darker color for the scrollbar thumb. -->
<color name="car_scrollbar_thumb_light">#99ffffff</color>
<color name="car_scrollbar_thumb_dark">#7f0b0f12</color>
diff --git a/car/res/values/dimens.xml b/car/res/values/dimens.xml
index 471d669..d7bf4ad 100644
--- a/car/res/values/dimens.xml
+++ b/car/res/values/dimens.xml
@@ -14,6 +14,14 @@
limitations under the License.
-->
<resources>
+ <!-- Keylines for content. -->
+ <dimen name="car_keyline_1">24dp</dimen>
+
+ <!-- Various text stylings. -->
+ <dimen name="car_title_size">26sp</dimen>
+ <dimen name="car_body1_size">32sp</dimen>
+ <dimen name="car_body2_size">26sp</dimen>
+
<!-- The margin on both sides of the screen before the contents of the PagedListView. -->
<dimen name="car_card_margin">96dp</dimen>
@@ -54,4 +62,43 @@
<!-- The maximum the scrollbar thumb can grow to -->
<dimen name="max_thumb_height">128dp</dimen>
+
+ <!-- Size of progress-bar in Drawer -->
+ <dimen name="car_drawer_progress_bar_size">48dp</dimen>
+
+ <!-- The ending margin of the drawer. Is is the amount that the navigation drawer does not
+ cover the screen. -->
+ <dimen name="car_drawer_margin_end">96dp</dimen>
+
+ <!-- Dimensions of the back arrow in the drawer. -->
+ <dimen name="car_arrow_size">96dp</dimen>
+ <dimen name="car_arrow_thickness">3dp</dimen>
+ <dimen name="car_arrow_shaft_length">34dp</dimen>
+ <dimen name="car_arrow_head_length">18dp</dimen>
+ <dimen name="car_menu_bar_spacing">6dp</dimen>
+ <dimen name="car_menu_bar_length">40dp</dimen>
+
+ <!-- The height of an individual item in the navigation drawer. -->
+ <dimen name="car_drawer_list_item_height">88dp</dimen>
+
+ <!-- The height of a small list item. -->
+ <dimen name="car_drawer_list_item_height_small">64dp</dimen>
+
+ <!-- The size of the starting icon. -->
+ <dimen name="car_drawer_list_item_icon_size">64dp</dimen>
+
+ <!-- The margin after the starting icon. -->
+ <dimen name="car_drawer_list_item_icon_end_margin">32dp</dimen>
+
+ <!-- The ending margin on a list view. -->
+ <dimen name="car_drawer_list_item_end_margin">32dp</dimen>
+
+ <!-- The size of the starting icon in a small list item.-->
+ <dimen name="car_drawer_list_item_small_icon_size">56dp</dimen>
+
+ <!-- The size of the ending icon in a list item. -->
+ <dimen name="car_drawer_list_item_end_icon_size">56dp</dimen>
+
+ <!-- The margin between text is lies on top of each other. -->
+ <dimen name="car_text_vertical_margin">2dp</dimen>
</resources>
diff --git a/car/res/values/strings.xml b/car/res/values/strings.xml
new file mode 100644
index 0000000..65f08b6
--- /dev/null
+++ b/car/res/values/strings.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+<resources>
+ <!-- NOTE: Although these strings won't really be used for accessibility
+ in an auto context, integration tests will use them to open/close
+ drawer. See:
+ google_testing/integration/libraries/app-helpers/first-party/auto/
+ -->
+ <string name="car_drawer_open" translatable="false">Open drawer</string>
+ <string name="car_drawer_close" translatable="false">Close drawer</string>
+</resources>
diff --git a/car/res/values/styles.xml b/car/res/values/styles.xml
new file mode 100644
index 0000000..2fd9348
--- /dev/null
+++ b/car/res/values/styles.xml
@@ -0,0 +1,60 @@
+<?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.
+-->
+<resources>
+ <!-- The styling for title text. The color of this text changes based on day/night mode. -->
+ <style name="CarTitle" >
+ <item name="android:textStyle">normal</item>
+ <item name="android:textSize">@dimen/car_title_size</item>
+ <item name="android:textColor">@color/car_title</item>
+ </style>
+
+ <!-- Title text that is permanently a dark color. -->
+ <style name="CarTitle.Dark" >
+ <item name="android:textColor">@color/car_title_dark</item>
+ </style>
+
+ <!-- Title text that is permanently a light color. -->
+ <style name="CarTitle.Light" >
+ <item name="android:textColor">@color/car_title_light</item>
+ </style>
+
+ <!-- The styling for body text. The color of this text changes based on the day/night mode. -->
+ <style name="CarBody1" >
+ <item name="android:textStyle">normal</item>
+ <item name="android:textSize">@dimen/car_body1_size</item>
+ <item name="android:textColor">@color/car_body1</item>
+ </style>
+
+ <!-- An alternate styling for body text that is both a different color and size than
+ CarBody1. -->
+ <style name="CarBody2" >
+ <item name="android:textStyle">normal</item>
+ <item name="android:textSize">@dimen/car_body2_size</item>
+ <item name="android:textColor">@color/car_body2</item>
+ </style>
+
+ <!-- The style for the menu bar (i.e. hamburger) and back arrow in the navigation drawer. -->
+ <style name="DrawerArrowStyle" parent="Widget.AppCompat.DrawerArrowToggle">
+ <item name="color">@color/car_title_light</item>
+ <item name="spinBars">true</item>
+ <item name="barLength">@dimen/car_menu_bar_length</item>
+ <item name="thickness">@dimen/car_arrow_thickness</item>
+ <item name="gapBetweenBars">@dimen/car_menu_bar_spacing</item>
+ <item name="arrowShaftLength">@dimen/car_arrow_shaft_length</item>
+ <item name="arrowHeadLength">@dimen/car_arrow_head_length</item>
+ <item name="drawableSize">@dimen/car_arrow_size</item>
+ </style>
+</resources>
diff --git a/car/res/values/themes.xml b/car/res/values/themes.xml
new file mode 100644
index 0000000..4244a22
--- /dev/null
+++ b/car/res/values/themes.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+<resources>
+ <!-- A Theme that activities should use to have correct arrow styling. -->
+ <style name="CarDrawerActivityTheme" parent="Theme.AppCompat.Light.NoActionBar">
+ <item name="drawerArrowStyle">@style/DrawerArrowStyle</item>
+ </style>
+
+ <!-- The styling for the action bar. -->
+ <style name="CarToolbarTheme">
+ <item name="titleTextAppearance">@style/CarTitle.Light</item>
+ <item name="contentInsetStart">@dimen/car_keyline_1</item>
+ <item name="contentInsetEnd">@dimen/car_keyline_1</item>
+ </style>
+</resources>
diff --git a/car/src/main/java/android/support/car/drawer/CarDrawerActivity.java b/car/src/main/java/android/support/car/drawer/CarDrawerActivity.java
new file mode 100644
index 0000000..b6cfc08
--- /dev/null
+++ b/car/src/main/java/android/support/car/drawer/CarDrawerActivity.java
@@ -0,0 +1,139 @@
+/*
+ * 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.
+ */
+
+package android.support.car.drawer;
+
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.support.annotation.LayoutRes;
+import android.support.car.R;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v7.app.ActionBarDrawerToggle;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Common base Activity for car apps that need to present a Drawer.
+ *
+ * <p>This Activity manages the overall layout. To use it, sub-classes need to:
+ *
+ * <ul>
+ * <li>Provide the root-items for the Drawer by implementing {@link #getRootAdapter()}.
+ * <li>Add their main content using {@link #setMainContent(int)} or {@link #setMainContent(View)}.
+ * They can also add fragments to the main-content container by obtaining its id using
+ * {@link #getContentContainerId()}
+ * </ul>
+ *
+ * <p>This class will take care of drawer toggling and display.
+ *
+ * <p>The rootAdapter can implement nested-navigation, in its click-handling, by passing the
+ * CarDrawerAdapter for the next level to
+ * {@link CarDrawerController#switchToAdapter(CarDrawerAdapter)}.
+ *
+ * <p>Any Activity's based on this class need to set their theme to CarDrawerActivityTheme or a
+ * derivative.
+ */
+public abstract class CarDrawerActivity extends AppCompatActivity {
+ protected CarDrawerController mDrawerController;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.car_drawer_activity);
+
+ DrawerLayout drawerLayout = findViewById(R.id.drawer_layout);
+ ActionBarDrawerToggle drawerToggle = new ActionBarDrawerToggle(
+ this /* activity */,
+ drawerLayout, /* DrawerLayout object */
+ R.string.car_drawer_open,
+ R.string.car_drawer_close);
+
+ Toolbar toolbar = findViewById(R.id.car_toolbar);
+ setSupportActionBar(toolbar);
+
+ mDrawerController = new CarDrawerController(toolbar, drawerLayout, drawerToggle);
+ mDrawerController.setRootAdapter(getRootAdapter());
+
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setHomeButtonEnabled(true);
+ }
+
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState) {
+ super.onPostCreate(savedInstanceState);
+ mDrawerController.syncState();
+ }
+
+ /**
+ * @return Adapter for root content of the Drawer.
+ */
+ protected abstract CarDrawerAdapter getRootAdapter();
+
+ /**
+ * Set main content to display in this Activity. It will be added to R.id.content_frame in
+ * car_drawer_activity.xml. NOTE: Do not use {@link #setContentView(View)}.
+ *
+ * @param view View to display as main content.
+ */
+ public void setMainContent(View view) {
+ ViewGroup parent = findViewById(getContentContainerId());
+ parent.addView(view);
+ }
+
+ /**
+ * Set main content to display in this Activity. It will be added to R.id.content_frame in
+ * car_drawer_activity.xml. NOTE: Do not use {@link #setContentView(int)}.
+ *
+ * @param resourceId Layout to display as main content.
+ */
+ public void setMainContent(@LayoutRes int resourceId) {
+ ViewGroup parent = findViewById(getContentContainerId());
+ LayoutInflater inflater = getLayoutInflater();
+ inflater.inflate(resourceId, parent, true);
+ }
+
+ /**
+ * Get the id of the main content Container which is a FrameLayout. Subclasses can add their own
+ * content/fragments inside here.
+ *
+ * @return Id of FrameLayout where main content of the subclass Activity can be added.
+ */
+ protected int getContentContainerId() {
+ return R.id.content_frame;
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ mDrawerController.closeDrawer();
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ mDrawerController.onConfigurationChanged(newConfig);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ return mDrawerController.onOptionsItemSelected(item) || super.onOptionsItemSelected(item);
+ }
+}
diff --git a/car/src/main/java/android/support/car/drawer/CarDrawerAdapter.java b/car/src/main/java/android/support/car/drawer/CarDrawerAdapter.java
new file mode 100644
index 0000000..b0fd965
--- /dev/null
+++ b/car/src/main/java/android/support/car/drawer/CarDrawerAdapter.java
@@ -0,0 +1,182 @@
+/*
+ * 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.
+ */
+
+package android.support.car.drawer;
+
+import android.content.Context;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.car.R;
+import android.support.car.widget.PagedListView;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Base adapter for displaying items in the car navigation drawer, which uses a
+ * {@link PagedListView}.
+ *
+ * <p>Subclasses must set the title that will be displayed when displaying the contents of the
+ * drawer via {@link #setTitle(CharSequence)}. The title can be updated at any point later on. The
+ * title of the root adapter will also be the main title showed in the toolbar when the drawer is
+ * closed. See {@link CarDrawerController#setRootAdapter(CarDrawerAdapter)} for more information.
+ *
+ * <p>This class also takes care of implementing the PageListView.ItemCamp contract and subclasses
+ * should implement {@link #getActualItemCount()}.
+ */
+public abstract class CarDrawerAdapter extends RecyclerView.Adapter<DrawerItemViewHolder>
+ implements PagedListView.ItemCap, DrawerItemClickListener {
+ private final boolean mShowDisabledListOnEmpty;
+ private final Drawable mEmptyListDrawable;
+ private int mMaxItems = PagedListView.ItemCap.UNLIMITED;
+ private CharSequence mTitle;
+ private TitleChangeListener mTitleChangeListener;
+
+ /**
+ * Interface for a class that will be notified a new title has been set on this adapter.
+ */
+ interface TitleChangeListener {
+ /**
+ * Called when {@link #setTitle(CharSequence)} has been called and the title has been
+ * changed.
+ */
+ void onTitleChanged(CharSequence newTitle);
+ }
+
+ protected CarDrawerAdapter(Context context, boolean showDisabledListOnEmpty) {
+ mShowDisabledListOnEmpty = showDisabledListOnEmpty;
+
+ mEmptyListDrawable = context.getDrawable(R.drawable.ic_list_view_disable);
+ mEmptyListDrawable.setColorFilter(context.getColor(R.color.car_tint),
+ PorterDuff.Mode.SRC_IN);
+ }
+
+ /** Returns the title set via {@link #setTitle(CharSequence)}. */
+ CharSequence getTitle() {
+ return mTitle;
+ }
+
+ /** Updates the title to display in the toolbar for this Adapter. */
+ public final void setTitle(@NonNull CharSequence title) {
+ if (title == null) {
+ throw new IllegalArgumentException("setTitle() cannot be passed a null title!");
+ }
+
+ mTitle = title;
+
+ if (mTitleChangeListener != null) {
+ mTitleChangeListener.onTitleChanged(mTitle);
+ }
+ }
+
+ /** Sets a listener to be notified whenever the title of this adapter has been changed. */
+ void setTitleChangeListener(@Nullable TitleChangeListener listener) {
+ mTitleChangeListener = listener;
+ }
+
+ @Override
+ public final void setMaxItems(int maxItems) {
+ mMaxItems = maxItems;
+ }
+
+ @Override
+ public final int getItemCount() {
+ if (shouldShowDisabledListItem()) {
+ return 1;
+ }
+ return mMaxItems >= 0 ? Math.min(mMaxItems, getActualItemCount()) : getActualItemCount();
+ }
+
+ /**
+ * Returns the absolute number of items that can be displayed in the list.
+ *
+ * <p>A class should implement this method to supply the number of items to be displayed.
+ * Returning 0 from this method will cause an empty list icon to be displayed in the drawer.
+ *
+ * <p>A class should override this method rather than {@link #getItemCount()} because that
+ * method is handling the logic of when to display the empty list icon. It will return 1 when
+ * {@link #getActualItemCount()} returns 0.
+ *
+ * @return The number of items to be displayed in the list.
+ */
+ protected abstract int getActualItemCount();
+
+ @Override
+ public final int getItemViewType(int position) {
+ if (shouldShowDisabledListItem()) {
+ return R.layout.car_drawer_list_item_empty;
+ }
+
+ return usesSmallLayout(position)
+ ? R.layout.car_drawer_list_item_small
+ : R.layout.car_drawer_list_item_normal;
+ }
+
+ /**
+ * Used to indicate the layout used for the Drawer item at given position. Subclasses can
+ * override this to use normal layout which includes text element below title.
+ *
+ * <p>A small layout is presented by the layout {@code R.layout.car_drawer_list_item_small}.
+ * Otherwise, the layout {@code R.layout.car_drawer_list_item_normal} will be used.
+ *
+ * @param position Adapter position of item.
+ * @return Whether the item at this position will use a small layout (default) or normal layout.
+ */
+ protected boolean usesSmallLayout(int position) {
+ return true;
+ }
+
+ @Override
+ public final DrawerItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
+ return new DrawerItemViewHolder(view);
+ }
+
+ @Override
+ public final void onBindViewHolder(DrawerItemViewHolder holder, int position) {
+ if (shouldShowDisabledListItem()) {
+ holder.getTitle().setText(null);
+ holder.getIcon().setImageDrawable(mEmptyListDrawable);
+ holder.setItemClickListener(null);
+ } else {
+ holder.setItemClickListener(this);
+ populateViewHolder(holder, position);
+ }
+ }
+
+ /**
+ * Whether or not this adapter should be displaying an empty list icon. The icon is shown if it
+ * has been configured to show and there are no items to be displayed.
+ */
+ private boolean shouldShowDisabledListItem() {
+ return mShowDisabledListOnEmpty && getActualItemCount() == 0;
+ }
+
+ /**
+ * Subclasses should set all elements in {@code holder} to populate the drawer-item. If some
+ * element is not used, it should be nulled out since these ViewHolder/View's are recycled.
+ */
+ protected abstract void populateViewHolder(DrawerItemViewHolder holder, int position);
+
+ /**
+ * Called when this adapter has been popped off the stack and is no longer needed. Subclasses
+ * can override to do any necessary cleanup.
+ */
+ public void cleanup() {}
+}
diff --git a/car/src/main/java/android/support/car/drawer/CarDrawerController.java b/car/src/main/java/android/support/car/drawer/CarDrawerController.java
new file mode 100644
index 0000000..b7c9817
--- /dev/null
+++ b/car/src/main/java/android/support/car/drawer/CarDrawerController.java
@@ -0,0 +1,298 @@
+/*
+ * 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.
+ */
+
+package android.support.car.drawer;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.car.R;
+import android.support.car.widget.PagedListView;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v7.app.ActionBarDrawerToggle;
+import android.support.v7.widget.Toolbar;
+import android.view.Gravity;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.ProgressBar;
+
+import java.util.Stack;
+
+/**
+ * A controller that will handle the set up of the navigation drawer. It will hook up the
+ * necessary buttons for up navigation, as well as expose methods to allow for a drill down
+ * navigation.
+ */
+public class CarDrawerController {
+ /** The amount that the drawer has been opened before its color should be switched. */
+ private static final float COLOR_SWITCH_SLIDE_OFFSET = 0.25f;
+
+ /**
+ * A representation of the hierarchy of navigation being displayed in the list. The ordering of
+ * this stack is the order that the user has visited each level. When the user navigates up,
+ * the adapters are poopped from this list.
+ */
+ private final Stack<CarDrawerAdapter> mAdapterStack = new Stack<>();
+
+ private final Context mContext;
+
+ private final Toolbar mToolbar;
+ private final DrawerLayout mDrawerLayout;
+ private final ActionBarDrawerToggle mDrawerToggle;
+
+ private final PagedListView mDrawerList;
+ private final ProgressBar mProgressBar;
+ private final View mDrawerContent;
+
+ /**
+ * Creates a {@link CarDrawerController} that will control the navigation of the drawer given by
+ * {@code drawerLayout}.
+ *
+ * <p>The given {@code drawerLayout} should either have a child View that is inflated from
+ * {@code R.layout.car_drawer} or ensure that it three children that have the IDs found in that
+ * layout.
+ *
+ * @param toolbar The {@link Toolbar} that will serve as the action bar for an Activity.
+ * @param drawerLayout The top-level container for the window content that shows the
+ * interactive drawer.
+ * @param drawerToggle The {@link ActionBarDrawerToggle} that bridges the given {@code toolbar}
+ * and {@code drawerLayout}.
+ */
+ public CarDrawerController(Toolbar toolbar,
+ DrawerLayout drawerLayout,
+ ActionBarDrawerToggle drawerToggle) {
+ mToolbar = toolbar;
+ mContext = drawerLayout.getContext();
+
+ mDrawerLayout = drawerLayout;
+
+ mDrawerContent = drawerLayout.findViewById(R.id.drawer_content);
+ mDrawerList = drawerLayout.findViewById(R.id.drawer_list);
+ mDrawerList.setMaxPages(PagedListView.ItemCap.UNLIMITED);
+
+ mProgressBar = drawerLayout.findViewById(R.id.drawer_progress);
+
+ mDrawerToggle = drawerToggle;
+ setupDrawerToggling();
+ }
+
+ /**
+ * Sets the {@link CarDrawerAdapter} that will function as the root adapter. The contents of
+ * this root adapter are shown when the drawer is first opened. It is also the top-most level of
+ * navigation in the drawer.
+ */
+ public void setRootAdapter(CarDrawerAdapter rootAdapter) {
+ mAdapterStack.push(rootAdapter);
+ setToolbarTitleFrom(rootAdapter);
+ mDrawerList.setAdapter(rootAdapter);
+ }
+
+ /**
+ * Switches to use the given {@link CarDrawerAdapter} as the one to supply the list to display
+ * in the navigation drawer. The title will also be updated from the adapter.
+ *
+ * <p>This switch is treated as a navigation to the next level in the drawer. Navigation away
+ * from this level will pop the given adapter off and surface contents of the previous adapter
+ * that was set via this method. If no such adapter exists, then the root adapter set by
+ * {@link #setRootAdapter(CarDrawerAdapter)} will be used instead.
+ *
+ * @param adapter Adapter for next level of content in the drawer.
+ */
+ public final void switchToAdapter(CarDrawerAdapter adapter) {
+ mAdapterStack.peek().setTitleChangeListener(null);
+ mAdapterStack.push(adapter);
+ switchToAdapterInternal(adapter);
+ }
+
+ /** Close the drawer. */
+ public void closeDrawer() {
+ if (mDrawerLayout.isDrawerOpen(Gravity.LEFT)) {
+ mDrawerLayout.closeDrawer(Gravity.LEFT);
+ }
+ }
+
+ /** Opens the drawer. */
+ public void openDrawer() {
+ if (!mDrawerLayout.isDrawerOpen(Gravity.LEFT)) {
+ mDrawerLayout.openDrawer(Gravity.LEFT);
+ }
+ }
+
+ /** Sets a listener to be notified of Drawer events. */
+ public void addDrawerListener(@NonNull DrawerLayout.DrawerListener listener) {
+ mDrawerLayout.addDrawerListener(listener);
+ }
+
+ /** Removes a listener to be notified of Drawer events. */
+ public void removeDrawerListener(@NonNull DrawerLayout.DrawerListener listener) {
+ mDrawerLayout.removeDrawerListener(listener);
+ }
+
+ /**
+ * Sets whether the loading progress bar is displayed in the navigation drawer. If {@code true},
+ * the progress bar is displayed and the navigation list is hidden and vice versa.
+ */
+ public void showLoadingProgressBar(boolean show) {
+ mDrawerList.setVisibility(show ? View.INVISIBLE : View.VISIBLE);
+ mProgressBar.setVisibility(show ? View.VISIBLE : View.GONE);
+ }
+
+ /** Scroll to given position in the list. */
+ public void scrollToPosition(int position) {
+ mDrawerList.getRecyclerView().smoothScrollToPosition(position);
+ }
+
+ /**
+ * Retrieves the title from the given {@link CarDrawerAdapter} and set its as the title of this
+ * controller's internal Toolbar.
+ */
+ private void setToolbarTitleFrom(CarDrawerAdapter adapter) {
+ if (adapter.getTitle() == null) {
+ throw new RuntimeException("CarDrawerAdapter must supply a title via setTitle()");
+ }
+
+ mToolbar.setTitle(adapter.getTitle());
+ adapter.setTitleChangeListener(mToolbar::setTitle);
+ }
+
+ /**
+ * Sets up the necessary listeners for {@link DrawerLayout} so that the navigation drawer
+ * hierarchy is properly displayed.
+ */
+ private void setupDrawerToggling() {
+ mDrawerLayout.addDrawerListener(mDrawerToggle);
+ mDrawerLayout.addDrawerListener(
+ new DrawerLayout.DrawerListener() {
+ @Override
+ public void onDrawerSlide(View drawerView, float slideOffset) {
+ // Correctly set the title and arrow colors as they are different between
+ // the open and close states.
+ updateTitleAndArrowColor(slideOffset >= COLOR_SWITCH_SLIDE_OFFSET);
+ }
+
+ @Override
+ public void onDrawerClosed(View drawerView) {
+ // If drawer is closed, revert stack/drawer to initial root state.
+ cleanupStackAndShowRoot();
+ scrollToPosition(0);
+ }
+
+ @Override
+ public void onDrawerOpened(View drawerView) {}
+
+ @Override
+ public void onDrawerStateChanged(int newState) {}
+ });
+ }
+
+ /** Sets the title and arrow color of the drawer depending on if it is open or not. */
+ private void updateTitleAndArrowColor(boolean drawerOpen) {
+ // When the drawer is open, use car_title, which resolves to appropriate color depending on
+ // day-night mode. When drawer is closed, we always use light color.
+ int titleColorResId = drawerOpen ? R.color.car_title : R.color.car_title_light;
+ int titleColor = mContext.getColor(titleColorResId);
+ mToolbar.setTitleTextColor(titleColor);
+ mDrawerToggle.getDrawerArrowDrawable().setColor(titleColor);
+ }
+
+ /**
+ * Synchronizes the display of the drawer with its linked {@link DrawerLayout}.
+ *
+ * <p>This should be called from the associated Activity's
+ * {@link android.support.v7.app.AppCompatActivity#onPostCreate(Bundle)} method to synchronize
+ * after teh DRawerLayout's instance state has been restored, and any other time when the
+ * state may have diverged in such a way that this controller's associated
+ * {@link ActionBarDrawerToggle} had not been notified.
+ */
+ public void syncState() {
+ mDrawerToggle.syncState();
+
+ // In case we're restarting after a config change (e.g. day, night switch), set colors
+ // again. Doing it here so that Drawer state is fully synced and we know if its open or not.
+ // NOTE: isDrawerOpen must be passed the second child of the DrawerLayout.
+ updateTitleAndArrowColor(mDrawerLayout.isDrawerOpen(mDrawerContent));
+ }
+
+ /**
+ * Notify this controller that device configurations may have changed.
+ *
+ * <p>This method should be called from the associated Activity's
+ * {@code onConfigurationChanged()} method.
+ */
+ public void onConfigurationChanged(Configuration newConfig) {
+ // Pass any configuration change to the drawer toggle.
+ mDrawerToggle.onConfigurationChanged(newConfig);
+ }
+
+ /**
+ * An analog to an Activity's {@code onOptionsItemSelected()}. This method should be called
+ * when the Activity's method is called and will return {@code true} if the selection has
+ * been handled.
+ *
+ * @return {@code true} if the item processing was handled by this class.
+ */
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle home-click and see if we can navigate up in the drawer.
+ if (item != null && item.getItemId() == android.R.id.home && maybeHandleUpClick()) {
+ return true;
+ }
+
+ // DrawerToggle gets next chance to handle up-clicks (and any other clicks).
+ return mDrawerToggle.onOptionsItemSelected(item);
+ }
+
+ /**
+ * Sets the navigation drawer's title to be the one supplied by the given adapter and updates
+ * the navigation drawer list with the adapter's contents.
+ */
+ private void switchToAdapterInternal(CarDrawerAdapter adapter) {
+ setToolbarTitleFrom(adapter);
+ // NOTE: We don't use swapAdapter() since different levels in the Drawer may switch between
+ // car_drawer_list_item_normal, car_drawer_list_item_small and car_list_empty layouts.
+ mDrawerList.getRecyclerView().setAdapter(adapter);
+ scrollToPosition(0);
+ }
+
+ /**
+ * Switches to the previous level in the drawer hierarchy if the current list being displayed
+ * is not the root adapter. This is analogous to a navigate up.
+ *
+ * @return {@code true} if a navigate up was possible and executed. {@code false} otherwise.
+ */
+ private boolean maybeHandleUpClick() {
+ // Check if already at the root level.
+ if (mAdapterStack.size() <= 1) {
+ return false;
+ }
+
+ CarDrawerAdapter adapter = mAdapterStack.pop();
+ adapter.setTitleChangeListener(null);
+ adapter.cleanup();
+ switchToAdapterInternal(mAdapterStack.peek());
+ return true;
+ }
+
+ /** Clears stack down to root adapter and switches to root adapter. */
+ private void cleanupStackAndShowRoot() {
+ while (mAdapterStack.size() > 1) {
+ CarDrawerAdapter adapter = mAdapterStack.pop();
+ adapter.setTitleChangeListener(null);
+ adapter.cleanup();
+ }
+ switchToAdapterInternal(mAdapterStack.peek());
+ }
+}
diff --git a/car/src/main/java/android/support/car/drawer/DrawerItemClickListener.java b/car/src/main/java/android/support/car/drawer/DrawerItemClickListener.java
new file mode 100644
index 0000000..d707dbd
--- /dev/null
+++ b/car/src/main/java/android/support/car/drawer/DrawerItemClickListener.java
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+package android.support.car.drawer;
+
+/**
+ * Listener for handling clicks on items/views managed by {@link DrawerItemViewHolder}.
+ */
+public interface DrawerItemClickListener {
+ /**
+ * Callback when item is clicked.
+ *
+ * @param position Adapter position of the clicked item.
+ */
+ void onItemClick(int position);
+}
diff --git a/car/src/main/java/android/support/car/drawer/DrawerItemViewHolder.java b/car/src/main/java/android/support/car/drawer/DrawerItemViewHolder.java
new file mode 100644
index 0000000..d016b2d
--- /dev/null
+++ b/car/src/main/java/android/support/car/drawer/DrawerItemViewHolder.java
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+package android.support.car.drawer;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.car.R;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+/**
+ * Re-usable {@link RecyclerView.ViewHolder} for displaying items in the
+ * {@link android.support.car.drawer.CarDrawerAdapter}.
+ */
+public class DrawerItemViewHolder extends RecyclerView.ViewHolder {
+ private final ImageView mIcon;
+ private final TextView mTitle;
+ private final TextView mText;
+ private final ImageView mEndIcon;
+
+ DrawerItemViewHolder(View view) {
+ super(view);
+ mIcon = view.findViewById(R.id.icon);
+ if (mIcon == null) {
+ throw new IllegalArgumentException("Icon view cannot be null!");
+ }
+
+ mTitle = view.findViewById(R.id.title);
+ if (mTitle == null) {
+ throw new IllegalArgumentException("Title view cannot be null!");
+ }
+
+ // Next two are optional and may be null.
+ mText = view.findViewById(R.id.text);
+ mEndIcon = view.findViewById(R.id.end_icon);
+ }
+
+ /** Returns the view that should be used to display the main icon. */
+ @NonNull
+ public ImageView getIcon() {
+ return mIcon;
+ }
+
+ /** Returns the view that will display the main title. */
+ @NonNull
+ public TextView getTitle() {
+ return mTitle;
+ }
+
+ /** Returns the view that is used for text that is smaller than the title text. */
+ @Nullable
+ public TextView getText() {
+ return mText;
+ }
+
+ /** Returns the icon that is displayed at the end of the view. */
+ @Nullable
+ public ImageView getEndIcon() {
+ return mEndIcon;
+ }
+
+ /**
+ * Sets the listener that will be notified when the view held by this ViewHolder has been
+ * clicked. Passing {@code null} will clear any previously set listeners.
+ */
+ void setItemClickListener(@Nullable DrawerItemClickListener listener) {
+ itemView.setOnClickListener(listener != null
+ ? v -> listener.onItemClick(getAdapterPosition())
+ : null);
+ }
+}
diff --git a/car/src/main/java/android/support/car/widget/PagedListView.java b/car/src/main/java/android/support/car/widget/PagedListView.java
index 8527c65..d87c943 100644
--- a/car/src/main/java/android/support/car/widget/PagedListView.java
+++ b/car/src/main/java/android/support/car/widget/PagedListView.java
@@ -27,7 +27,6 @@
import android.support.annotation.IdRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
-import android.support.annotation.Px;
import android.support.annotation.RestrictTo;
import android.support.annotation.UiThread;
import android.support.car.R;
@@ -61,7 +60,6 @@
protected final CarLayoutManager mLayoutManager;
protected final Handler mHandler = new Handler();
private final boolean mScrollBarEnabled;
- private final boolean mRightGutterEnabled;
private final PagedScrollBarView mScrollBarView;
private int mRowsPerPage = -1;
@@ -98,6 +96,11 @@
*/
public interface ItemCap {
/**
+ * A value to pass to {@link #setMaxItems(int)} that indicates there should be no limit.
+ */
+ int UNLIMITED = -1;
+
+ /**
* Sets the maximum number of items available in the adapter. A value less than '0' means
* the list should not be capped.
*/
@@ -139,7 +142,6 @@
}
LayoutInflater.from(context).inflate(layoutId, this /*root*/, true /*attachToRoot*/);
- FrameLayout maxWidthLayout = (FrameLayout) findViewById(R.id.recycler_view_container);
TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.PagedListView, defStyleAttrs, defStyleRes);
mRecyclerView = (CarRecyclerView) findViewById(R.id.recycler_view);
@@ -156,6 +158,16 @@
mRecyclerView.getRecycledViewPool().setMaxRecycledViews(0, 12);
mRecyclerView.setItemAnimator(new CarItemAnimator(mLayoutManager));
+ boolean offsetScrollBar = a.getBoolean(R.styleable.PagedListView_offsetScrollBar, false);
+ if (offsetScrollBar) {
+ MarginLayoutParams params = (MarginLayoutParams) mRecyclerView.getLayoutParams();
+ params.setMarginStart(getResources().getDimensionPixelSize(
+ R.dimen.car_paged_list_view_pagination_width));
+ params.setMarginEnd(
+ a.getDimensionPixelSize(R.styleable.PagedListView_listEndMargin, 0));
+ mRecyclerView.setLayoutParams(params);
+ }
+
if (a.getBoolean(R.styleable.PagedListView_showPagedListViewDivider, true)) {
int dividerStartMargin = a.getDimensionPixelSize(
R.styleable.PagedListView_dividerStartMargin, 0);
@@ -199,47 +211,20 @@
}
}
});
+
mScrollBarView.setVisibility(mScrollBarEnabled ? VISIBLE : GONE);
- // Modify the layout if the Gutter or the Scroll Bar are not visible.
- mRightGutterEnabled = a.getBoolean(R.styleable.PagedListView_rightGutterEnabled, false);
- if (mRightGutterEnabled || !mScrollBarEnabled) {
- FrameLayout.LayoutParams maxWidthLayoutLayoutParams =
- (FrameLayout.LayoutParams) maxWidthLayout.getLayoutParams();
- if (mRightGutterEnabled) {
- maxWidthLayoutLayoutParams.rightMargin =
- getResources().getDimensionPixelSize(R.dimen.car_card_margin);
- }
- if (!mScrollBarEnabled) {
- maxWidthLayoutLayoutParams.setMarginStart(0);
- }
- maxWidthLayout.setLayoutParams(maxWidthLayoutLayoutParams);
+ // Modify the layout the Scroll Bar is not visible.
+ if (!mScrollBarEnabled) {
+ MarginLayoutParams params = (MarginLayoutParams) mRecyclerView.getLayoutParams();
+ params.setMarginStart(0);
+ mRecyclerView.setLayoutParams(params);
}
setDayNightStyle(DayNightStyle.AUTO);
a.recycle();
}
- /**
- * Sets the starting and ending padding for each view in the list.
- *
- * @param start The start padding.
- * @param end The end padding.
- */
- public void setListViewStartEndPadding(@Px int start, @Px int end) {
- int carCardMargin = getResources().getDimensionPixelSize(R.dimen.car_card_margin);
- int startGutter = mScrollBarEnabled ? carCardMargin : 0;
- int startPadding = Math.max(start - startGutter, 0);
- int endGutter = mRightGutterEnabled ? carCardMargin : 0;
- int endPadding = Math.max(end - endGutter, 0);
- mRecyclerView.setPaddingRelative(startPadding, mRecyclerView.getPaddingTop(),
- endPadding, mRecyclerView.getPaddingBottom());
-
- // Since we're setting padding we'll need to set the clip to padding to the same
- // value as clip children to ensure that the cards fly off the screen.
- mRecyclerView.setClipToPadding(mRecyclerView.getClipChildren());
- }
-
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();