Add PlaybackOverlayFragment and associated row and presenters.

Progress bar is not functional yet.

Change-Id: I6217385433d7c6621c68de68da93257efebe3c5b
diff --git a/v17/leanback/res/drawable-v21/lb_control_button_primary.xml b/v17/leanback/res/drawable-v21/lb_control_button_primary.xml
new file mode 100644
index 0000000..eb081a1
--- /dev/null
+++ b/v17/leanback/res/drawable-v21/lb_control_button_primary.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 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="?android:attr/colorControlHighlight" >
+
+    <item android:id="@android:id/mask">
+        <selector android:constantSize="true" >
+            <item android:state_focused="true">
+                <shape android:shape="oval" >
+
+                    <!-- Color not used since this is a ripple mask -->
+                    <solid android:color="@color/lb_control_button_color" />
+
+                    <size
+                        android:height="@dimen/lb_control_button_diameter"
+                        android:width="@dimen/lb_control_button_diameter" />
+                </shape>
+            </item>
+        </selector>
+    </item>
+
+</ripple>
\ No newline at end of file
diff --git a/v17/leanback/res/drawable-v21/lb_control_button_secondary.xml b/v17/leanback/res/drawable-v21/lb_control_button_secondary.xml
new file mode 100644
index 0000000..6c67206
--- /dev/null
+++ b/v17/leanback/res/drawable-v21/lb_control_button_secondary.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 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="?android:attr/colorControlHighlight" >
+
+    <item android:id="@android:id/mask">
+        <selector android:constantSize="true" >
+            <item android:state_focused="true">
+                <shape android:shape="oval" >
+
+                    <!-- Color not used since this is a ripple mask -->
+                    <solid android:color="@color/lb_control_button_color" />
+
+                    <size
+                        android:height="@dimen/lb_control_button_secondary_diameter"
+                        android:width="@dimen/lb_control_button_secondary_diameter" />
+                </shape>
+            </item>
+        </selector>
+    </item>
+
+</ripple>
\ No newline at end of file
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_cc.png b/v17/leanback/res/drawable-xhdpi/lb_ic_cc.png
new file mode 100644
index 0000000..518c063
--- /dev/null
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_cc.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_fast_forward.png b/v17/leanback/res/drawable-xhdpi/lb_ic_fast_forward.png
new file mode 100644
index 0000000..c5afb69
--- /dev/null
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_fast_forward.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_fast_rewind.png b/v17/leanback/res/drawable-xhdpi/lb_ic_fast_rewind.png
new file mode 100644
index 0000000..d9774ef
--- /dev/null
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_fast_rewind.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_hq.png b/v17/leanback/res/drawable-xhdpi/lb_ic_hq.png
new file mode 100644
index 0000000..f797d38
--- /dev/null
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_hq.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_loop.png b/v17/leanback/res/drawable-xhdpi/lb_ic_loop.png
new file mode 100644
index 0000000..1676892
--- /dev/null
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_loop.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_more.png b/v17/leanback/res/drawable-xhdpi/lb_ic_more.png
new file mode 100644
index 0000000..ef62a69
--- /dev/null
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_more.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_pause.png b/v17/leanback/res/drawable-xhdpi/lb_ic_pause.png
new file mode 100644
index 0000000..01e07f7
--- /dev/null
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_pause.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_play.png b/v17/leanback/res/drawable-xhdpi/lb_ic_play.png
new file mode 100644
index 0000000..1556076
--- /dev/null
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_play.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_playback_loop.png b/v17/leanback/res/drawable-xhdpi/lb_ic_playback_loop.png
new file mode 100644
index 0000000..f444ca4
--- /dev/null
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_playback_loop.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_replay.png b/v17/leanback/res/drawable-xhdpi/lb_ic_replay.png
new file mode 100644
index 0000000..a76d8fe
--- /dev/null
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_replay.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_shuffle.png b/v17/leanback/res/drawable-xhdpi/lb_ic_shuffle.png
new file mode 100644
index 0000000..32cf2dc
--- /dev/null
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_shuffle.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_skip_next.png b/v17/leanback/res/drawable-xhdpi/lb_ic_skip_next.png
new file mode 100644
index 0000000..2173375
--- /dev/null
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_skip_next.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_skip_previous.png b/v17/leanback/res/drawable-xhdpi/lb_ic_skip_previous.png
new file mode 100644
index 0000000..2c6035a
--- /dev/null
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_skip_previous.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_stop.png b/v17/leanback/res/drawable-xhdpi/lb_ic_stop.png
new file mode 100644
index 0000000..b96f297
--- /dev/null
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_stop.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_thumb_down.png b/v17/leanback/res/drawable-xhdpi/lb_ic_thumb_down.png
new file mode 100644
index 0000000..85dd902
--- /dev/null
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_thumb_down.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_thumb_down_outline.png b/v17/leanback/res/drawable-xhdpi/lb_ic_thumb_down_outline.png
new file mode 100644
index 0000000..2208fdf
--- /dev/null
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_thumb_down_outline.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_thumb_up.png b/v17/leanback/res/drawable-xhdpi/lb_ic_thumb_up.png
new file mode 100644
index 0000000..b24b146
--- /dev/null
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_thumb_up.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_thumb_up_outline.png b/v17/leanback/res/drawable-xhdpi/lb_ic_thumb_up_outline.png
new file mode 100644
index 0000000..d6a5240
--- /dev/null
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_thumb_up_outline.png
Binary files differ
diff --git a/v17/leanback/res/drawable/lb_control_button_primary.xml b/v17/leanback/res/drawable/lb_control_button_primary.xml
new file mode 100644
index 0000000..4528f95
--- /dev/null
+++ b/v17/leanback/res/drawable/lb_control_button_primary.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 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" android:constantSize="true" >
+    <item android:state_focused="true">
+        <shape android:shape="oval">
+            <solid android:color="@color/lb_control_button_color" />
+            <size
+                android:height="@dimen/lb_control_button_diameter"
+                android:width="@dimen/lb_control_button_diameter" />
+        </shape>
+    </item>
+</selector>
diff --git a/v17/leanback/res/drawable/lb_control_button_secondary.xml b/v17/leanback/res/drawable/lb_control_button_secondary.xml
new file mode 100644
index 0000000..9e52645
--- /dev/null
+++ b/v17/leanback/res/drawable/lb_control_button_secondary.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 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" android:constantSize="true" >
+    <item android:state_focused="true">
+        <shape android:shape="oval">
+            <solid android:color="@color/lb_control_button_color" />
+            <size
+                android:height="@dimen/lb_control_button_secondary_diameter"
+                android:width="@dimen/lb_control_button_secondary_diameter" />
+        </shape>
+    </item>
+</selector>
diff --git a/v17/leanback/res/drawable/lb_playback_progress_bar.xml b/v17/leanback/res/drawable/lb_playback_progress_bar.xml
new file mode 100644
index 0000000..8cbb03c
--- /dev/null
+++ b/v17/leanback/res/drawable/lb_playback_progress_bar.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 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.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <item android:id="@android:id/background">
+        <shape>
+            <solid android:color="#16FFFFFF" />
+
+            <corners
+                android:bottomLeftRadius="0dp"
+                android:bottomRightRadius="0dp"
+                android:topLeftRadius="2dp"
+                android:topRightRadius="2dp" />
+        </shape>
+    </item>
+    <item android:id="@android:id/secondaryProgress">
+        <clip>
+            <shape>
+                <solid android:color="#1DFFFFFF" />
+
+                <corners
+                    android:bottomLeftRadius="0dp"
+                    android:bottomRightRadius="0dp"
+                    android:topLeftRadius="2dp"
+                    android:topRightRadius="2dp" />
+            </shape>
+        </clip>
+    </item>
+    <item android:id="@android:id/progress">
+        <clip>
+            <shape>
+                <solid android:color="#00ff00" />
+
+                <corners
+                    android:bottomLeftRadius="0dp"
+                    android:bottomRightRadius="0dp"
+                    android:topLeftRadius="2dp"
+                    android:topRightRadius="2dp" />
+            </shape>
+        </clip>
+    </item>
+
+</layer-list>
diff --git a/v17/leanback/res/layout/lb_control_bar.xml b/v17/leanback/res/layout/lb_control_bar.xml
new file mode 100644
index 0000000..4b82dcc
--- /dev/null
+++ b/v17/leanback/res/layout/lb_control_bar.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 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/control_bar"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal" />
diff --git a/v17/leanback/res/layout/lb_control_button_primary.xml b/v17/leanback/res/layout/lb_control_button_primary.xml
new file mode 100644
index 0000000..86e3e0d
--- /dev/null
+++ b/v17/leanback/res/layout/lb_control_button_primary.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 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="wrap_content"
+    android:layout_height="wrap_content" >
+
+    <ImageView
+        android:id="@+id/button"
+        style="?attr/playbackControlsButtonStyle"
+        android:layout_width="@dimen/lb_control_button_diameter"
+        android:layout_height="@dimen/lb_control_button_height"
+        android:scaleType="center"
+        android:src="@drawable/lb_control_button_primary" />
+
+    <ImageView
+        android:id="@+id/icon"
+        android:layout_width="@dimen/lb_control_icon_width"
+        android:layout_height="@dimen/lb_control_icon_height"
+        android:layout_gravity="center" />
+
+</FrameLayout>
\ No newline at end of file
diff --git a/v17/leanback/res/layout/lb_control_button_secondary.xml b/v17/leanback/res/layout/lb_control_button_secondary.xml
new file mode 100644
index 0000000..4d0cafd
--- /dev/null
+++ b/v17/leanback/res/layout/lb_control_button_secondary.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 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="wrap_content"
+    android:layout_height="wrap_content" >
+
+    <ImageView
+        android:id="@+id/button"
+        style="?attr/playbackControlsButtonStyle"
+        android:layout_width="@dimen/lb_control_button_secondary_diameter"
+        android:layout_height="@dimen/lb_control_button_secondary_height"
+        android:scaleType="center"
+        android:src="@drawable/lb_control_button_secondary" />
+
+    <ImageView
+        android:id="@+id/icon"
+        android:layout_width="@dimen/lb_control_icon_width"
+        android:layout_height="@dimen/lb_control_icon_height"
+        android:layout_gravity="center" />
+
+</FrameLayout>
\ No newline at end of file
diff --git a/v17/leanback/res/layout/lb_playback_controls.xml b/v17/leanback/res/layout/lb_playback_controls.xml
new file mode 100644
index 0000000..bdc049c
--- /dev/null
+++ b/v17/leanback/res/layout/lb_playback_controls.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 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="wrap_content"
+    android:orientation="vertical" >
+
+    <ProgressBar
+        android:id="@+id/playback_progress"
+        style="?android:attr/progressBarStyleHorizontal"
+        android:layout_width="match_parent"
+        android:layout_height="4dp"
+        android:maxHeight="4dp"
+        android:minHeight="4dp"
+        android:progressDrawable="@drawable/lb_playback_progress_bar" />
+
+    <FrameLayout
+        android:id="@+id/controls_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" >
+
+        <LinearLayout
+            android:id="@+id/control_bar"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:orientation="horizontal" />
+
+        <FrameLayout
+            android:id="@+id/more_actions_dock"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="right" />
+
+        <TextView
+            android:id="@+id/current_time"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="top|left"
+            android:layout_marginLeft="16dp"
+            android:fontFamily="sans-serif"
+            android:paddingTop="12dp"
+            android:textColor="#b2eeeeee"
+            android:textSize="12sp" />
+
+        <TextView
+            android:id="@+id/total_time"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="top|right"
+            android:layout_marginRight="16dp"
+            android:fontFamily="sans-serif"
+            android:paddingTop="12dp"
+            android:textColor="#b2eeeeee"
+            android:textSize="12sp" />
+    </FrameLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/v17/leanback/res/layout/lb_playback_controls_row.xml b/v17/leanback/res/layout/lb_playback_controls_row.xml
new file mode 100644
index 0000000..a4484a2
--- /dev/null
+++ b/v17/leanback/res/layout/lb_playback_controls_row.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 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="wrap_content"
+    android:orientation="vertical"
+    android:paddingBottom="@dimen/lb_playback_controls_margin_bottom"
+    android:paddingLeft="@dimen/lb_playback_controls_margin_left"
+    android:paddingRight="@dimen/lb_playback_controls_margin_right" >
+
+    <!-- Background is applied to this inner layout -->
+
+    <LinearLayout
+        android:id="@+id/controls_card"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/lb_playback_controls_card_height"
+        android:orientation="horizontal" >
+
+        <ImageView
+            android:id="@+id/image"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:adjustViewBounds="true"
+            android:scaleType="fitStart" />
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:orientation="vertical" >
+
+            <FrameLayout
+                android:id="@+id/description_dock"
+                android:layout_width="wrap_content"
+                android:layout_height="0dp"
+                android:layout_marginEnd="@dimen/lb_playback_description_margin_end"
+                android:layout_marginStart="@dimen/lb_playback_description_margin_start"
+                android:layout_marginTop="@dimen/lb_playback_description_margin_top"
+                android:layout_weight="1"
+                android:gravity="top" />
+
+            <!-- Space here prevents room only for title and subtitle above -->
+
+            <Space
+                android:id="@+id/spacer"
+                android:layout_width="match_parent"
+                android:layout_height="12dp" />
+
+            <FrameLayout
+                android:id="@+id/controls_dock"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginEnd="@dimen/lb_playback_description_margin_end"
+                android:layout_marginStart="@dimen/lb_playback_description_margin_start" />
+
+        </LinearLayout>
+    </LinearLayout>
+
+    <FrameLayout
+        android:id="@+id/secondary_controls_dock"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_horizontal"
+        android:layout_marginTop="@dimen/lb_secondary_controls_margin_top" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/v17/leanback/res/values/attrs.xml b/v17/leanback/res/values/attrs.xml
index ecacc55..0713edb 100644
--- a/v17/leanback/res/values/attrs.xml
+++ b/v17/leanback/res/values/attrs.xml
@@ -180,6 +180,9 @@
         <attr name="detailsDescriptionBodyStyle" format="reference" />
         <attr name="detailsActionButtonStyle" format="reference" />
 
+        <!-- for playback controls -->
+        <attr name="playbackControlsButtonStyle" format="reference" />
+
         <!-- style for a vertical grid of items -->
         <attr name="itemsVerticalGridStyle" format="reference" />
 
diff --git a/v17/leanback/res/values/colors.xml b/v17/leanback/res/values/colors.xml
index 5fc7e0a..912f87e 100644
--- a/v17/leanback/res/values/colors.xml
+++ b/v17/leanback/res/values/colors.xml
@@ -52,4 +52,7 @@
 
     <color name="lb_default_brand_color">#FF455A64</color>
     <color name="lb_default_search_color">#FFFFAA3F</color>
+
+    <color name="lb_control_button_color">#66EEEEEE</color>
+
 </resources>
diff --git a/v17/leanback/res/values/dimens.xml b/v17/leanback/res/values/dimens.xml
index 30bc8e8..ce8fc74 100644
--- a/v17/leanback/res/values/dimens.xml
+++ b/v17/leanback/res/values/dimens.xml
@@ -104,6 +104,23 @@
     <dimen name="lb_action_icon_margin">12dp</dimen>
     <dimen name="lb_action_text_size">16sp</dimen>
 
+    <dimen name="lb_playback_controls_align_bottom">48dp</dimen>
+    <dimen name="lb_playback_controls_card_height">176dp</dimen>
+    <dimen name="lb_playback_controls_margin_left">132dp</dimen>
+    <dimen name="lb_playback_controls_margin_right">132dp</dimen>
+    <dimen name="lb_playback_controls_margin_bottom">20dp</dimen>
+    <dimen name="lb_playback_description_margin_top">24dp</dimen>
+    <dimen name="lb_playback_description_margin_start">24dp</dimen>
+    <dimen name="lb_playback_description_margin_end">24dp</dimen>
+    <dimen name="lb_secondary_controls_margin_top">12dp</dimen>
+
+    <dimen name="lb_control_button_diameter">76dp</dimen>
+    <dimen name="lb_control_button_height">64dp</dimen>
+    <dimen name="lb_control_button_secondary_diameter">48dp</dimen>
+    <dimen name="lb_control_button_secondary_height">48dp</dimen>
+    <dimen name="lb_control_icon_width">32dp</dimen>
+    <dimen name="lb_control_icon_height">32dp</dimen>
+
     <dimen name="lb_error_image_max_height">120dp</dimen>
     <integer name="lb_error_message_max_lines">1</integer>
     <dimen name="lb_error_message_max_width">600dp</dimen>
diff --git a/v17/leanback/res/values/ids.xml b/v17/leanback/res/values/ids.xml
index 05a6304..6bcff36 100644
--- a/v17/leanback/res/values/ids.xml
+++ b/v17/leanback/res/values/ids.xml
@@ -17,4 +17,16 @@
  <resources>
      <item type="id" name="lb_focus_animator" />
      <item type="id" name="lb_header_transition_position" />
- </resources>
\ No newline at end of file
+
+     <item type="id" name="lb_control_play_pause" />
+     <item type="id" name="lb_control_fast_forward" />
+     <item type="id" name="lb_control_fast_rewind" />
+     <item type="id" name="lb_control_skip_next" />
+     <item type="id" name="lb_control_skip_previous" />
+     <item type="id" name="lb_control_more_actions" />
+     <item type="id" name="lb_control_thumbs_up" />
+     <item type="id" name="lb_control_thumbs_down" />
+     <item type="id" name="lb_control_repeat" />
+     <item type="id" name="lb_control_shuffle" />
+
+ </resources>
diff --git a/v17/leanback/res/values/styles.xml b/v17/leanback/res/values/styles.xml
index 1d43795..245d424 100644
--- a/v17/leanback/res/values/styles.xml
+++ b/v17/leanback/res/values/styles.xml
@@ -212,7 +212,7 @@
         <item name="android:ellipsize">end</item>
     </style>
 
-    <style name="Widget.Leanback.DetailsActionButtonStyle" parent="android:Widget.Holo.Button.Borderless">
+    <style name="Widget.Leanback.DetailsActionButtonStyle" parent="android:Widget.Material.Button.Borderless">
         <item name="android:textAppearance">@style/TextAppearance.Leanback.DetailsActionButton</item>
         <item name="android:includeFontPadding">false</item>
         <item name="android:drawablePadding">@dimen/lb_action_icon_margin</item>
@@ -222,6 +222,11 @@
         <item name="android:paddingRight">@dimen/lb_action_padding_horizontal</item>
     </style>
 
+    <style name="Widget.Leanback.PlaybackControlsButtonStyle" >
+        <item name="android:focusable">true</item>
+        <item name="android:focusableInTouchMode">true</item>
+    </style>
+
     <style name="Widget.Leanback.ErrorMessageStyle">
         <item name="android:textAppearance">@style/TextAppearance.Leanback.ErrorMessage</item>
         <item name="android:includeFontPadding">false</item>
diff --git a/v17/leanback/res/values/themes.xml b/v17/leanback/res/values/themes.xml
index fee4967..b13e720 100644
--- a/v17/leanback/res/values/themes.xml
+++ b/v17/leanback/res/values/themes.xml
@@ -57,6 +57,7 @@
         <item name="detailsDescriptionSubtitleStyle">@style/Widget.Leanback.DetailsDescriptionSubtitleStyle</item>
         <item name="detailsDescriptionBodyStyle">@style/Widget.Leanback.DetailsDescriptionBodyStyle</item>
         <item name="detailsActionButtonStyle">@style/Widget.Leanback.DetailsActionButtonStyle</item>
+        <item name="playbackControlsButtonStyle">@style/Widget.Leanback.PlaybackControlsButtonStyle</item>
 
         <item name="errorMessageStyle">@style/Widget.Leanback.ErrorMessageStyle</item>
 
diff --git a/v17/leanback/src/android/support/v17/leanback/app/DetailsFragment.java b/v17/leanback/src/android/support/v17/leanback/app/DetailsFragment.java
index c06cf1c..1b04c8f 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/DetailsFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/DetailsFragment.java
@@ -154,7 +154,7 @@
         }
     };
 
-    private void setVerticalGridViewLayout(VerticalGridView listview) {
+    void setVerticalGridViewLayout(VerticalGridView listview) {
         // align the top edge of item to a fixed position
         listview.setItemAlignmentOffset(0);
         listview.setItemAlignmentOffsetPercent(VerticalGridView.ITEM_ALIGN_OFFSET_PERCENT_DISABLED);
@@ -163,13 +163,16 @@
         listview.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE);
     }
 
+    VerticalGridView getVerticalGridView() {
+        return mRowsFragment == null ? null : mRowsFragment.getVerticalGridView();
+    }
+
     /**
      * Setup dimensions that are only meaningful when the child Fragments are inside
      * DetailsFragment.
      */
     private void setupChildFragmentLayout() {
-        VerticalGridView containerList = mRowsFragment.getVerticalGridView();
-        setVerticalGridViewLayout(containerList);
+        setVerticalGridViewLayout(mRowsFragment.getVerticalGridView());
     }
 
     @Override
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlayFragment.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlayFragment.java
new file mode 100644
index 0000000..988e47e
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlayFragment.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2014 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.v17.leanback.app;
+
+import android.os.Bundle;
+import android.support.v17.leanback.R;
+import android.support.v17.leanback.widget.ObjectAdapter;
+import android.support.v17.leanback.widget.VerticalGridView;
+
+
+/**
+ * A fragment for displaying playback controls and related content.
+ */
+public class PlaybackOverlayFragment extends DetailsFragment {
+
+    private int mAlignPosition;
+
+    /**
+     * Sets the list of rows for the fragment.
+     */
+    @Override
+    public void setAdapter(ObjectAdapter adapter) {
+        super.setAdapter(adapter);
+        setVerticalGridViewLayout(getVerticalGridView());
+    }
+
+    @Override
+    void setVerticalGridViewLayout(VerticalGridView listview) {
+        if (listview == null || getAdapter() == null) {
+            return;
+        }
+        final int alignPosition = getAdapter().size() > 1 ? mAlignPosition : 0;
+        listview.setItemAlignmentOffset(alignPosition);
+        listview.setItemAlignmentOffsetPercent(100);
+        listview.setWindowAlignmentOffset(0);
+        listview.setWindowAlignmentOffsetPercent(100);
+        listview.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_HIGH_EDGE);
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mAlignPosition =
+            getResources().getDimensionPixelSize(R.dimen.lb_playback_controls_align_bottom);
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ControlBarPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/ControlBarPresenter.java
new file mode 100644
index 0000000..5bdcf25
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ControlBarPresenter.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2014 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.v17.leanback.widget;
+
+import android.support.v17.leanback.R;
+import android.util.SparseArray;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+/**
+ * A presenter that assumes a LinearLayout container for a series
+ * of control buttons backed by objects of type {@link Action}.
+ *
+ * Different layouts may be passed to the presenter constructor.
+ * The layout must contain a view with id control_bar.
+ */
+class ControlBarPresenter extends Presenter {
+
+    private static final int MAX_CONTROLS = 7;
+
+    /**
+     * The data type expected by this presenter.
+     */
+    static class BoundData {
+        /**
+         * Adapter containing objects of type {@link Action}.
+         */
+        ObjectAdapter adapter;
+
+        /**
+         * The presenter to be used for the adapter objects.
+         */
+        Presenter presenter;
+    }
+
+    /**
+     * A ViewHolder for an actions bar.
+     */
+    class ViewHolder extends Presenter.ViewHolder {
+        ObjectAdapter mAdapter;
+        Presenter mPresenter;
+        LinearLayout mControlBar;
+        SparseArray<Presenter.ViewHolder> mViewHolders =
+                new SparseArray<Presenter.ViewHolder>();
+        ObjectAdapter.DataObserver mDataObserver;
+
+        /**
+         * Constructor for the ViewHolder.
+         */
+        ViewHolder(View rootView) {
+            super(rootView);
+            mControlBar = (LinearLayout) rootView.findViewById(R.id.control_bar);
+            if (mControlBar == null) {
+                throw new IllegalStateException("Couldn't find control_bar");
+            }
+            mDataObserver = new ObjectAdapter.DataObserver() {
+                @Override
+                public void onChanged() {
+                    showControls(mAdapter, mPresenter);
+                }
+                @Override
+                public void onItemRangeChanged(int positionStart, int itemCount) {
+                    for (int i = 0; i < itemCount; i++) {
+                        bindControlToAction(positionStart + i, mAdapter, mPresenter);
+                    }
+                }
+            };
+        }
+
+        void showControls(ObjectAdapter adapter, Presenter presenter) {
+            View focusedChild = mControlBar.getFocusedChild();
+            mControlBar.removeAllViews();
+            for (int position = 0; position < adapter.size() && position < MAX_CONTROLS;
+                    position++) {
+                bindControlToAction(position, adapter, presenter);
+            }
+            if (focusedChild != null) {
+                focusedChild.requestFocus();
+            }
+        }
+
+        void bindControlToAction(final int position, ObjectAdapter adapter, Presenter presenter) {
+            Presenter.ViewHolder vh = mViewHolders.get(position);
+            Object item = adapter.get(position);
+            if (vh == null) {
+                vh = presenter.onCreateViewHolder(mControlBar);
+                mViewHolders.put(position, vh);
+                presenter.setOnClickListener(vh, new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        Object item = getAdapter().get(position);
+                        if (mOnActionClickedListener != null && item instanceof Action) {
+                            mOnActionClickedListener.onActionClicked((Action) item);
+                        }
+                    }
+                });
+            }
+            if (vh.view.getParent() == null) {
+                mControlBar.addView(vh.view);
+            }
+            presenter.onBindViewHolder(vh, item);
+        }
+
+        ObjectAdapter getAdapter() {
+            return mAdapter;
+        }
+    }
+
+    private OnActionClickedListener mOnActionClickedListener;
+    private int mLayoutResourceId;
+
+    /**
+     * Constructor for a ControlBarPresenter.
+     *
+     * @param layoutResourceId The resource id of the layout for this presenter.
+     */
+    public ControlBarPresenter(int layoutResourceId) {
+        mLayoutResourceId = layoutResourceId;
+    }
+
+    /**
+     * Returns the layout resource id.
+     */
+    public int getLayoutResourceId() {
+        return mLayoutResourceId;
+    }
+
+    /**
+     * Sets the listener for {@link Action} click events.
+     */
+    public void setOnActionClickedListener(OnActionClickedListener listener) {
+        mOnActionClickedListener = listener;
+    }
+
+    /**
+     * Gets the listener for {@link Action} click events.
+     */
+    public OnActionClickedListener getOnActionClickedListener() {
+        return mOnActionClickedListener;
+    }
+
+    @Override
+    public Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) {
+        View v = LayoutInflater.from(parent.getContext())
+            .inflate(getLayoutResourceId(), parent, false);
+        return new ViewHolder(v);
+    }
+
+    @Override
+    public void onBindViewHolder(Presenter.ViewHolder holder, Object item) {
+        ViewHolder vh = (ViewHolder) holder;
+        BoundData data = (BoundData) item;
+        if (vh.mAdapter != data.adapter) {
+            vh.mAdapter = data.adapter;
+            vh.mAdapter.registerObserver(vh.mDataObserver);
+        }
+        vh.mPresenter = data.presenter;
+        vh.showControls(vh.mAdapter, vh.mPresenter);
+    }
+
+    @Override
+    public void onUnbindViewHolder(Presenter.ViewHolder holder) {
+        ViewHolder vh = (ViewHolder) holder;
+        vh.mAdapter.unregisterObserver(vh.mDataObserver);
+        vh.mAdapter = null;
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ControlButtonPresenterSelector.java b/v17/leanback/src/android/support/v17/leanback/widget/ControlButtonPresenterSelector.java
new file mode 100644
index 0000000..de95729
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ControlButtonPresenterSelector.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2014 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.v17.leanback.widget;
+
+import android.support.v17.leanback.R;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+/**
+ * ControlButtonPresenterSelector displays primary and secondary
+ * controls for a {@link PlaybackControlsRow}.
+ *
+ * Binds to items of type {@link Action}.
+ */
+public class ControlButtonPresenterSelector extends PresenterSelector {
+
+    private final Presenter mPrimaryPresenter =
+            new ControlButtonPresenter(R.layout.lb_control_button_primary);
+    private final Presenter mSecondaryPresenter =
+            new ControlButtonPresenter(R.layout.lb_control_button_secondary);
+
+    /**
+     * Returns the presenter for primary controls.
+     */
+    public Presenter getPrimaryPresenter() {
+        return mPrimaryPresenter;
+    }
+
+    /**
+     * Returns the presenter for secondary controls.
+     */
+    public Presenter getSecondaryPresenter() {
+        return mSecondaryPresenter;
+    }
+
+    /**
+     * Always returns the presenter for primary controls.
+     */
+    public Presenter getPresenter(Object item) {
+        return mPrimaryPresenter;
+    }
+
+    static class ActionViewHolder extends Presenter.ViewHolder {
+        ImageView mIcon;
+        View mFocusableView;
+
+        public ActionViewHolder(View view) {
+            super(view);
+            mIcon = (ImageView) view.findViewById(R.id.icon);
+            mFocusableView = view.findViewById(R.id.button);
+        }
+    }
+
+    static class ControlButtonPresenter extends Presenter {
+        private int mLayoutResourceId;
+
+        ControlButtonPresenter(int layoutResourceId) {
+            mLayoutResourceId = layoutResourceId;
+        }
+
+        @Override
+        public ViewHolder onCreateViewHolder(ViewGroup parent) {
+            View v = LayoutInflater.from(parent.getContext())
+                    .inflate(mLayoutResourceId, parent, false);
+            return new ActionViewHolder(v);
+        }
+
+        @Override
+        public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
+            Action action = (Action) item;
+            ActionViewHolder vh = (ActionViewHolder) viewHolder;
+            vh.mIcon.setImageDrawable(action.getIcon());
+        }
+
+        @Override
+        public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
+            ActionViewHolder vh = (ActionViewHolder) viewHolder;
+            vh.mIcon.setImageDrawable(null);
+        }
+
+        @Override
+        public void setOnClickListener(Presenter.ViewHolder viewHolder,
+                View.OnClickListener listener) {
+            ((ActionViewHolder) viewHolder).mFocusableView.setOnClickListener(listener);
+        }
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsPresenter.java
new file mode 100644
index 0000000..018cb67
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsPresenter.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2014 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.v17.leanback.widget;
+
+import android.support.v17.leanback.R;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+/**
+ * A presenter for a control bar that supports "more actions",
+ * and toggling the set of controls between primary and secondary
+ * sets of {@link Actions}.
+ */
+class PlaybackControlsPresenter extends ControlBarPresenter {
+
+    /**
+     * The data type expected by this presenter.
+     */
+    static class BoundData extends ControlBarPresenter.BoundData {
+        /**
+         * The adapter containing secondary actions.
+         */
+        ObjectAdapter secondaryActionsAdapter;
+    }
+
+    class ViewHolder extends ControlBarPresenter.ViewHolder {
+        ObjectAdapter mMoreActionsAdapter;
+        ObjectAdapter.DataObserver mMoreActionsObserver;
+        FrameLayout mMoreActionsDock;
+        Presenter.ViewHolder mMoreActionsViewHolder;
+        boolean mMoreActionsShowing;
+
+        ViewHolder(View rootView) {
+            super(rootView);
+            mMoreActionsDock = (FrameLayout) rootView.findViewById(R.id.more_actions_dock);
+            mMoreActionsObserver = new ObjectAdapter.DataObserver() {
+                @Override
+                public void onChanged() {
+                    if (mMoreActionsShowing) {
+                        showControls(mMoreActionsAdapter, mPresenter);
+                    }
+                }
+                @Override
+                public void onItemRangeChanged(int positionStart, int itemCount) {
+                    if (mMoreActionsShowing) {
+                        for (int i = 0; i < itemCount; i++) {
+                            bindControlToAction(positionStart + i,
+                                    mMoreActionsAdapter, mPresenter);
+                        }
+                    }
+                }
+            };
+        }
+
+        void showMoreActions() {
+            if (mMoreActionsViewHolder == null) {
+                Action action = new PlaybackControlsRow.MoreActions(mMoreActionsDock.getContext());
+                mMoreActionsViewHolder = mPresenter.onCreateViewHolder(mMoreActionsDock);
+                mPresenter.onBindViewHolder(mMoreActionsViewHolder, action);
+                mPresenter.setOnClickListener(mMoreActionsViewHolder, new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        toggleMoreActions();
+                    }
+                });
+            }
+            mMoreActionsDock.addView(mMoreActionsViewHolder.view);
+        }
+
+        void toggleMoreActions() {
+            mMoreActionsShowing = !mMoreActionsShowing;
+            showControls(getAdapter(), mPresenter);
+        }
+
+        @Override
+        ObjectAdapter getAdapter() {
+            return mMoreActionsShowing ? mMoreActionsAdapter : mAdapter;
+        }
+    }
+
+    private boolean mMoreActionsEnabled = true;
+
+    /**
+     * Constructor for a PlaybackControlsRowPresenter.
+     *
+     * @param layoutResourceId The resource id of the layout for this presenter.
+     */
+    public PlaybackControlsPresenter(int layoutResourceId) {
+        super(layoutResourceId);
+    }
+
+    /**
+     * Enables the display of secondary actions.
+     * A "more actions" button will be displayed.  When "more actions" is selected,
+     * the primary actions are replaced with the secondary actions.
+     */
+    public void enableSecondaryActions(boolean enable) {
+        mMoreActionsEnabled = enable;
+    }
+
+    /**
+     * Returns true if secondary actions are enabled.
+     */
+    public boolean areMoreActionsEnabled() {
+        return mMoreActionsEnabled;
+    }
+
+    @Override
+    public Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) {
+        View v = LayoutInflater.from(parent.getContext())
+            .inflate(getLayoutResourceId(), parent, false);
+        return new ViewHolder(v);
+    }
+
+    @Override
+    public void onBindViewHolder(Presenter.ViewHolder holder, Object item) {
+        super.onBindViewHolder(holder, item);
+
+        ViewHolder vh = (ViewHolder) holder;
+        BoundData data = (BoundData) item;
+        if (vh.mMoreActionsAdapter != data.secondaryActionsAdapter) {
+            vh.mMoreActionsAdapter = data.secondaryActionsAdapter;
+            vh.mMoreActionsAdapter.registerObserver(vh.mMoreActionsObserver);
+        }
+        if (mMoreActionsEnabled) {
+            vh.showMoreActions();
+        }
+    }
+
+    @Override
+    public void onUnbindViewHolder(Presenter.ViewHolder holder) {
+        super.onUnbindViewHolder(holder);
+        ViewHolder vh = (ViewHolder) holder;
+        vh.mMoreActionsAdapter.unregisterObserver(vh.mMoreActionsObserver);
+        vh.mMoreActionsAdapter = null;
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRow.java b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRow.java
new file mode 100644
index 0000000..56e08ac
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRow.java
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2014 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.v17.leanback.widget;
+
+import android.support.v17.leanback.R;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+
+/**
+ * A row of playback controls to be displayed by a {@link PlaybackControlsRowPresenter}.
+ *
+ * This row consists of some optional item detail, a series of primary actions,
+ * and an optional series of secondary actions.
+ *
+ * Controls are specified via an {@link ObjectAdapter} containing one or more
+ * {@link Action}s.
+ *
+ * Adapters should have their {@link PresenterSelector} set to an instance of
+ * {@link ControlButtonPresenterSelector}.
+ *
+ */
+public class PlaybackControlsRow extends Row {
+
+    /**
+     * An action displaying icons for play and pause.
+     */
+    public static class PlayPauseAction extends Action {
+        Drawable mPlayIcon;
+        Drawable mPauseIcon;
+
+        /**
+         * Constructor
+         * @param context Context used for loading resources.
+         */
+        public PlayPauseAction(Context context) {
+            super(R.id.lb_control_play_pause);
+            mPlayIcon = context.getResources().getDrawable(R.drawable.lb_ic_play);
+            mPauseIcon = context.getResources().getDrawable(R.drawable.lb_ic_pause);
+            play();
+        }
+
+        /**
+         * Display the play icon.
+         */
+        public void play() {
+            setIcon(mPlayIcon);
+        }
+
+        /**
+         * Display the pause icon.
+         */
+        public void pause() {
+            setIcon(mPauseIcon);
+        }
+
+        /**
+         * Toggle between the play and pause icon.
+         */
+        public void toggle() {
+            setIcon(getIcon() == mPlayIcon ? mPauseIcon : mPlayIcon);
+        }
+    }
+
+    /**
+     * An action displaying an icon for fast forward.
+     */
+    public static class FastForwardAction extends Action {
+        /**
+         * Constructor
+         * @param context Context used for loading resources.
+         */
+        public FastForwardAction(Context context) {
+            super(R.id.lb_control_fast_forward);
+            setIcon(context.getResources().getDrawable(R.drawable.lb_ic_fast_forward));
+        }
+    }
+
+    /**
+     * An action displaying an icon for rewind.
+     */
+    public static class RewindAction extends Action {
+        /**
+         * Constructor
+         * @param context Context used for loading resources.
+         */
+        public RewindAction(Context context) {
+            super(R.id.lb_control_fast_rewind);
+            setIcon(context.getResources().getDrawable(R.drawable.lb_ic_fast_rewind));
+        }
+    }
+
+    /**
+     * An action displaying an icon for skip next.
+     */
+    public static class SkipNextAction extends Action {
+        /**
+         * Constructor
+         * @param context Context used for loading resources.
+         */
+        public SkipNextAction(Context context) {
+            super(R.id.lb_control_skip_next);
+            setIcon(context.getResources().getDrawable(R.drawable.lb_ic_skip_next));
+        }
+    }
+
+    /**
+     * An action displaying an icon for skip previous.
+     */
+    public static class SkipPreviousAction extends Action {
+        /**
+         * Constructor
+         * @param context Context used for loading resources.
+         */
+        public SkipPreviousAction(Context context) {
+            super(R.id.lb_control_skip_previous);
+            setIcon(context.getResources().getDrawable(R.drawable.lb_ic_skip_previous));
+        }
+    }
+
+    /**
+     * An action displaying an icon for "more actions".
+     */
+    public static class MoreActions extends Action {
+        /**
+         * Constructor
+         * @param context Context used for loading resources.
+         */
+        public MoreActions(Context context) {
+            super(R.id.lb_control_more_actions);
+            setIcon(context.getResources().getDrawable(R.drawable.lb_ic_more));
+        }
+    }
+
+    /**
+     * An action displaying an icon for thumbs up.
+     */
+    public static class ThumbsUpAction extends Action {
+        /**
+         * Constructor
+         * @param context Context used for loading resources.
+         */
+        public ThumbsUpAction(Context context) {
+            super(R.id.lb_control_thumbs_up);
+            setIcon(context.getResources().getDrawable(R.drawable.lb_ic_thumb_up));
+        }
+    }
+
+    /**
+     * An action displaying an icon for thumbs down.
+     */
+    public static class ThumbsDownAction extends Action {
+        /**
+         * Constructor
+         * @param context Context used for loading resources.
+         */
+        public ThumbsDownAction(Context context) {
+            super(R.id.lb_control_thumbs_down);
+            setIcon(context.getResources().getDrawable(R.drawable.lb_ic_thumb_down));
+        }
+    }
+
+    /**
+     * An action for displaying three repeat states: none, one, or all.
+     */
+    public static class RepeatAction extends Action {
+        private static int NONE = 0;
+        private static int ONE = 1;
+        private static int ALL = 2;
+        private Drawable[] mRepeatIcon = new Drawable[3];
+        private int mState;
+
+        /**
+         * Constructor
+         * @param context Context used for loading resources.
+         */
+        public RepeatAction(Context context) {
+            super(R.id.lb_control_repeat);
+            Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(),
+                    R.drawable.lb_ic_loop);
+            mRepeatIcon[NONE] = new BitmapDrawable(context.getResources(),
+                    bitmap);
+            mRepeatIcon[ONE] = new BitmapDrawable(context.getResources(),
+                    createBitmap(bitmap, Color.CYAN));
+            mRepeatIcon[ALL] = new BitmapDrawable(context.getResources(),
+                    createBitmap(bitmap, Color.GREEN));
+            repeatNone();
+        }
+
+        /**
+         * Display the icon for repeat-none.
+         */
+        public void repeatNone() {
+            setIcon(mRepeatIcon[mState = NONE]);
+        }
+
+        /**
+         * Display the icon for repeat-one.
+         */
+        public void repeatOne() {
+            setIcon(mRepeatIcon[mState = ONE]);
+        }
+
+        /**
+         * Display the icon for repeat-all.
+         */
+        public void repeatAll() {
+            setIcon(mRepeatIcon[mState = ALL]);
+        }
+
+        /**
+         * Display the next icon in the series.
+         */
+        public void next() {
+            mState = mState == ALL ? NONE : mState + 1;
+            setIcon(mRepeatIcon[mState]);
+        }
+    }
+
+    /**
+     * An action for displaying a shuffle icon.
+     */
+    public static class ShuffleAction extends Action {
+        /**
+         * Constructor
+         * @param context Context used for loading resources.
+         */
+        public ShuffleAction(Context context) {
+            super(R.id.lb_control_shuffle);
+            setIcon(context.getResources().getDrawable(R.drawable.lb_ic_shuffle));
+        }
+    }
+
+    private static Bitmap createBitmap(Bitmap bitmap, int color) {
+        Bitmap dst = bitmap.copy(bitmap.getConfig(), true);
+        Canvas canvas = new Canvas(dst);
+        Paint paint = new Paint();
+        paint.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP));
+        canvas.drawBitmap(bitmap, 0, 0, paint);
+        return dst;
+    }
+
+    private Object mItem;
+    private Drawable mImageDrawable;
+    private ObjectAdapter mPrimaryActionsAdapter;
+    private ObjectAdapter mSecondaryActionsAdapter;
+
+    /**
+     * Constructor for a PlaybackControlsRow that displays some details from
+     * the given item.
+     *
+     * @param item The main item for the row.
+     */
+    public PlaybackControlsRow(Object item) {
+        mItem = item;
+    }
+
+    /**
+     * Constructor for a PlaybackControlsRow that has no item details.
+     */
+    public PlaybackControlsRow() {
+    }
+
+    /**
+     * Gets the main item for the details page.
+     */
+    public final Object getItem() {
+        return mItem;
+    }
+
+    /**
+     * Sets a {link @Drawable} image for this row.
+     *
+     * @param drawable The drawable to set.
+     */
+    public final void setImageDrawable(Drawable drawable) {
+        mImageDrawable = drawable;
+    }
+
+    /**
+     * Sets a {@link Bitmap} for this row.
+     *
+     * @param context The context to retrieve display metrics from.
+     * @param bm The bitmap to set.
+     */
+    public final void setImageBitmap(Context context, Bitmap bm) {
+        mImageDrawable = new BitmapDrawable(context.getResources(), bm);
+    }
+
+    /**
+     * Gets the image {@link Drawable} of this row.
+     *
+     * @return The overview's image drawable, or null if no drawable has been
+     *         assigned.
+     */
+    public final Drawable getImageDrawable() {
+        return mImageDrawable;
+    }
+
+    /**
+     * Sets the primary actions {@link ObjectAdapter}.
+     */
+    public final void setPrimaryActionsAdapter(ObjectAdapter adapter) {
+        mPrimaryActionsAdapter = adapter;
+    }
+
+    /**
+     * Sets the secondary actions {@link ObjectAdapter}.
+     */
+    public final void setSecondaryActionsAdapter(ObjectAdapter adapter) {
+        mSecondaryActionsAdapter = adapter;
+    }
+
+    /**
+     * Returns the primary actions {@link ObjectAdapter}.
+     */
+    public final ObjectAdapter getPrimaryActionsAdapter() {
+        return mPrimaryActionsAdapter;
+    }
+
+    /**
+     * Returns the secondary actions {@link ObjectAdapter}.
+     */
+    public final ObjectAdapter getSecondaryActionsAdapter() {
+        return mSecondaryActionsAdapter;
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRowPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRowPresenter.java
new file mode 100644
index 0000000..0f11a6a
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRowPresenter.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2014 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.v17.leanback.widget;
+
+import android.support.v17.leanback.R;
+import android.content.Context;
+import android.graphics.Color;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.view.ViewGroup.MarginLayoutParams;
+import android.widget.ImageView;
+
+/**
+ * A PlaybackControlsRowPresenter renders a {@link PlaybackControlsRow} to display a
+ * series of playback control buttons. Typically this row will be the first row in a fragment
+ * such as the {@link android.support.v17.leanback.app.PlaybackOverlayFragment
+ * PlaybackControlsFragment}.
+ */
+public class PlaybackControlsRowPresenter extends RowPresenter {
+
+    /**
+     * A ViewHolder for the PlaybackControlsRow.
+     */
+    public static class ViewHolder extends RowPresenter.ViewHolder {
+        final ViewGroup mCard;
+        final ImageView mImageView;
+        final ViewGroup mDescriptionDock;
+        final ViewGroup mControlsDock;
+        final ViewGroup mSecondaryControlsDock;
+        final View mSpacer;
+        int mCardHeight;
+        int mControlsDockMarginStart;
+        int mControlsDockMarginEnd;
+        Presenter.ViewHolder mDescriptionVh;
+        Presenter.ViewHolder mControlsVh;
+        Presenter.ViewHolder mSecondaryControlsVh;
+        PlaybackControlsPresenter.BoundData mControlsBoundData =
+                new PlaybackControlsPresenter.BoundData();
+        ControlBarPresenter.BoundData mSecondaryBoundData = new ControlBarPresenter.BoundData();
+
+        ViewHolder(View rootView) {
+            super(rootView);
+            mCard = (ViewGroup) rootView.findViewById(R.id.controls_card);
+            mImageView = (ImageView) rootView.findViewById(R.id.image);
+            mDescriptionDock = (ViewGroup) rootView.findViewById(R.id.description_dock);
+            mControlsDock = (ViewGroup) rootView.findViewById(R.id.controls_dock);
+            mSecondaryControlsDock =
+                    (ViewGroup) rootView.findViewById(R.id.secondary_controls_dock);
+            mSpacer = rootView.findViewById(R.id.spacer);
+        }
+
+        Presenter getPresenter(Object item, boolean primary) {
+            ObjectAdapter adapter = primary ?
+                    ((PlaybackControlsRow) getRow()).getPrimaryActionsAdapter() :
+                            ((PlaybackControlsRow) getRow()).getSecondaryActionsAdapter();
+            if (adapter.getPresenterSelector() instanceof ControlButtonPresenterSelector) {
+                ControlButtonPresenterSelector selector =
+                        (ControlButtonPresenterSelector) adapter.getPresenterSelector();
+                return primary ? selector.getPrimaryPresenter() :
+                    selector.getSecondaryPresenter();
+            }
+            return adapter.getPresenter(item);
+        }
+    }
+
+    private int mBackgroundColor = Color.TRANSPARENT;
+    private boolean mBackgroundColorSet;
+    private boolean mSecondaryActionsHidden;
+    private Presenter mDescriptionPresenter;
+    private PlaybackControlsPresenter mPlaybackControlsPresenter;
+    private ControlBarPresenter mSecondaryControlsPresenter;
+
+    /**
+     * Constructor for a PlaybackControlsRowPresenter.
+     *
+     * @param descriptionPresenter Presenter for displaying item details.
+     */
+    public PlaybackControlsRowPresenter(Presenter descriptionPresenter) {
+        setHeaderPresenter(null);
+        setSelectEffectEnabled(false);
+
+        mDescriptionPresenter = descriptionPresenter;
+        mPlaybackControlsPresenter = new PlaybackControlsPresenter(R.layout.lb_playback_controls);
+        mSecondaryControlsPresenter = new ControlBarPresenter(R.layout.lb_control_bar);
+    }
+
+    /**
+     * Constructor for a PlaybackControlsRowPresenter.
+     */
+    public PlaybackControlsRowPresenter() {
+        this(null);
+    }
+
+    /**
+     * Sets the listener for {@link Action} click events.
+     */
+    public void setOnActionClickedListener(OnActionClickedListener listener) {
+        mPlaybackControlsPresenter.setOnActionClickedListener(listener);
+        mSecondaryControlsPresenter.setOnActionClickedListener(listener);
+    }
+
+    /**
+     * Gets the listener for {@link Action} click events.
+     */
+    public OnActionClickedListener getOnActionClickedListener() {
+        return mPlaybackControlsPresenter.getOnActionClickedListener();
+    }
+
+    /**
+     * Sets the background color.  If not set, a default from the theme will be used.
+     */
+    public void setBackgroundColor(int color) {
+        mBackgroundColor = color;
+        mBackgroundColorSet = true;
+    }
+
+    /**
+     * Returns the background color.  If no background color was set, transparent
+     * is returned.
+     */
+    public int getBackgroundColor() {
+        return mBackgroundColor;
+    }
+
+    /**
+     * Sets the secondary actions to be hidden behind a "more actions" button.
+     * When "more actions" is selected, the primary actions are replaced with
+     * the secondary actions.
+     */
+    public void setSecondaryActionsHidden(boolean hidden) {
+        mSecondaryActionsHidden = hidden;
+    }
+
+    /**
+     * Returns true if secondary actions are hidden.
+     */
+    public boolean areSecondaryActionsHidden() {
+        return mSecondaryActionsHidden;
+    }
+
+    private int getDefaultBackgroundColor(Context context) {
+        TypedValue outValue = new TypedValue();
+        context.getTheme().resolveAttribute(R.attr.defaultBrandColor, outValue, true);
+        return context.getResources().getColor(outValue.resourceId);
+    }
+
+    @Override
+    protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) {
+        View v = LayoutInflater.from(parent.getContext())
+            .inflate(R.layout.lb_playback_controls_row, parent, false);
+        ViewHolder vh = new ViewHolder(v);
+        initRow(vh);
+        return vh;
+    }
+
+    private void initRow(ViewHolder vh) {
+        vh.mCardHeight = vh.mCard.getLayoutParams().height;
+
+        MarginLayoutParams lp = (MarginLayoutParams) vh.mControlsDock.getLayoutParams();
+        vh.mControlsDockMarginStart = lp.getMarginStart();
+        vh.mControlsDockMarginEnd = lp.getMarginEnd();
+
+        if (mDescriptionPresenter != null) {
+            vh.mDescriptionVh = mDescriptionPresenter.onCreateViewHolder(vh.mDescriptionDock);
+            vh.mDescriptionDock.addView(vh.mDescriptionVh.view);
+        }
+
+        vh.mControlsVh = mPlaybackControlsPresenter.onCreateViewHolder(vh.mControlsDock);
+        vh.mControlsDock.addView(vh.mControlsVh.view);
+
+        vh.mSecondaryControlsVh =
+                mSecondaryControlsPresenter.onCreateViewHolder(vh.mSecondaryControlsDock);
+        if (!mSecondaryActionsHidden) {
+            vh.mSecondaryControlsDock.addView(vh.mSecondaryControlsVh.view);
+        }
+    }
+
+    private void setBackground(View view) {
+        view.setBackgroundColor(mBackgroundColorSet ?
+                mBackgroundColor : getDefaultBackgroundColor(view.getContext()));
+        ShadowHelper.getInstance().setZ(view, 0f);
+    }
+
+    @Override
+    protected void onBindRowViewHolder(RowPresenter.ViewHolder holder, Object item) {
+        super.onBindRowViewHolder(holder, item);
+
+        ViewHolder vh = (ViewHolder) holder;
+        PlaybackControlsRow row = (PlaybackControlsRow) vh.getRow();
+
+        mPlaybackControlsPresenter.enableSecondaryActions(mSecondaryActionsHidden);
+
+        if (row.getItem() == null) {
+            LayoutParams lp = vh.mCard.getLayoutParams();
+            lp.height = LayoutParams.WRAP_CONTENT;
+            vh.mCard.setLayoutParams(lp);
+            vh.mDescriptionDock.setVisibility(View.GONE);
+            vh.mSpacer.setVisibility(View.GONE);
+        } else {
+            LayoutParams lp = vh.mCard.getLayoutParams();
+            lp.height = vh.mCardHeight;
+            vh.mCard.setLayoutParams(lp);
+            vh.mDescriptionDock.setVisibility(View.VISIBLE);
+            if (vh.mDescriptionVh != null) {
+                mDescriptionPresenter.onBindViewHolder(vh.mDescriptionVh, row.getItem());
+            }
+            vh.mSpacer.setVisibility(View.VISIBLE);
+        }
+
+        MarginLayoutParams lp = (MarginLayoutParams) vh.mControlsDock.getLayoutParams();
+        if (row.getImageDrawable() == null || row.getItem() == null) {
+            setBackground(vh.mControlsDock);
+            vh.mCard.setBackgroundColor(Color.TRANSPARENT);
+            lp.setMarginStart(0);
+            lp.setMarginEnd(0);
+        } else {
+            vh.mImageView.setImageDrawable(row.getImageDrawable());
+            setBackground(vh.mCard);
+            vh.mControlsDock.setBackgroundColor(Color.TRANSPARENT);
+            lp.setMarginStart(vh.mControlsDockMarginStart);
+            lp.setMarginEnd(vh.mControlsDockMarginEnd);
+        }
+        vh.mControlsDock.setLayoutParams(lp);
+
+        vh.mControlsBoundData.adapter = row.getPrimaryActionsAdapter();
+        vh.mControlsBoundData.secondaryActionsAdapter = row.getSecondaryActionsAdapter();
+        vh.mControlsBoundData.presenter = vh.getPresenter(
+                row.getPrimaryActionsAdapter().get(0), true);
+        mPlaybackControlsPresenter.onBindViewHolder(vh.mControlsVh, vh.mControlsBoundData);
+
+        vh.mSecondaryBoundData.adapter = row.getSecondaryActionsAdapter();
+        vh.mSecondaryBoundData.presenter = vh.getPresenter(
+                row.getSecondaryActionsAdapter().get(0), false);
+        mSecondaryControlsPresenter.onBindViewHolder(vh.mSecondaryControlsVh,
+                vh.mSecondaryBoundData);
+    }
+
+    @Override
+    protected void onUnbindRowViewHolder(RowPresenter.ViewHolder holder) {
+        super.onUnbindRowViewHolder(holder);
+
+        ViewHolder vh = (ViewHolder) holder;
+        if (vh.mDescriptionVh != null) {
+            mDescriptionPresenter.onUnbindViewHolder(vh.mDescriptionVh);
+        }
+        mPlaybackControlsPresenter.onUnbindViewHolder(vh.mControlsVh);
+        mSecondaryControlsPresenter.onUnbindViewHolder(vh.mSecondaryControlsVh);
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/Presenter.java b/v17/leanback/src/android/support/v17/leanback/widget/Presenter.java
index deffa45..ed7714d 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/Presenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/Presenter.java
@@ -108,4 +108,17 @@
     public void onViewDetachedFromWindow(ViewHolder holder) {
     }
 
+    /**
+     * Called to set a click listener for the given view holder.
+     *
+     * The default implementation sets the click listener on the root view in the view holder.
+     * If the root view isn't focusable this method should be overridden to set the listener
+     * on the appropriate focusable child view(s).
+     *
+     * @param holder The view holder containing the view(s) on which the listener should be set.
+     * @param listener The click listener to be set.
+     */
+    public void setOnClickListener(ViewHolder holder, View.OnClickListener listener) {
+        holder.view.setOnClickListener(listener);
+    }
 }