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);
+ }
}