summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Spurlock <jspurlock@google.com>2015-04-02 18:10:49 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2015-04-02 18:10:50 +0000
commit4a51c4c42c3e61beb9173f779fbcbc48f3511a6f (patch)
tree8c8e1351dbd6f937cd53bc3a18f9e7d7958eee97
parent0daa5713ee2df841ea22d88a1393ca11e0138621 (diff)
parentf88d8082a86bee00c604cbbcfb5261f5573936fe (diff)
downloadframeworks_base-4a51c4c42c3e61beb9173f779fbcbc48f3511a6f.zip
frameworks_base-4a51c4c42c3e61beb9173f779fbcbc48f3511a6f.tar.gz
frameworks_base-4a51c4c42c3e61beb9173f779fbcbc48f3511a6f.tar.bz2
Merge "Introduce new volume dialog."
-rw-r--r--packages/SystemUI/res/anim/ic_volume_collapse_chevron_02_animation.xml25
-rw-r--r--packages/SystemUI/res/anim/ic_volume_collapse_rectangle_1_animation.xml26
-rw-r--r--packages/SystemUI/res/anim/ic_volume_collapse_rectangle_2_animation.xml26
-rw-r--r--packages/SystemUI/res/anim/ic_volume_expand_chevron_01_animation.xml25
-rw-r--r--packages/SystemUI/res/anim/ic_volume_expand_rectangle_3_animation.xml26
-rw-r--r--packages/SystemUI/res/anim/ic_volume_expand_rectangle_4_animation.xml26
-rw-r--r--packages/SystemUI/res/drawable/btn_borderless_rect.xml10
-rw-r--r--packages/SystemUI/res/drawable/ic_dnd.xml26
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_minus.xml33
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_plus.xml33
-rw-r--r--packages/SystemUI/res/drawable/ic_volume_alarm.xml26
-rw-r--r--packages/SystemUI/res/drawable/ic_volume_alarm_mute.xml26
-rw-r--r--packages/SystemUI/res/drawable/ic_volume_bt.xml26
-rw-r--r--packages/SystemUI/res/drawable/ic_volume_bt_mute.xml26
-rw-r--r--packages/SystemUI/res/drawable/ic_volume_bt_sco.xml26
-rw-r--r--packages/SystemUI/res/drawable/ic_volume_collapse.xml62
-rw-r--r--packages/SystemUI/res/drawable/ic_volume_collapse_animation.xml29
-rw-r--r--packages/SystemUI/res/drawable/ic_volume_expand.xml62
-rw-r--r--packages/SystemUI/res/drawable/ic_volume_expand_animation.xml29
-rw-r--r--packages/SystemUI/res/drawable/ic_volume_media.xml26
-rw-r--r--packages/SystemUI/res/drawable/ic_volume_media_mute.xml29
-rw-r--r--packages/SystemUI/res/drawable/ic_volume_remote.xml26
-rw-r--r--packages/SystemUI/res/drawable/ic_volume_remote_mute.xml38
-rw-r--r--packages/SystemUI/res/drawable/ic_volume_ringer.xml26
-rw-r--r--packages/SystemUI/res/drawable/ic_volume_ringer_mute.xml26
-rw-r--r--packages/SystemUI/res/drawable/ic_volume_ringer_vibrate.xml26
-rw-r--r--packages/SystemUI/res/drawable/ic_volume_settings.xml26
-rw-r--r--packages/SystemUI/res/drawable/ic_volume_system.xml32
-rw-r--r--packages/SystemUI/res/drawable/ic_volume_system_mute.xml35
-rw-r--r--packages/SystemUI/res/drawable/ic_volume_voice.xml26
-rw-r--r--packages/SystemUI/res/drawable/volume_dialog_background.xml22
-rw-r--r--packages/SystemUI/res/interpolator/ic_volume_collapse_animation_interpolator_0.xml17
-rw-r--r--packages/SystemUI/res/interpolator/ic_volume_expand_animation_interpolator_0.xml17
-rw-r--r--packages/SystemUI/res/layout/segmented_button.xml8
-rw-r--r--packages/SystemUI/res/layout/volume_dialog.xml50
-rw-r--r--packages/SystemUI/res/layout/volume_dialog_row.xml64
-rw-r--r--packages/SystemUI/res/layout/volume_panel_dialog.xml27
-rw-r--r--packages/SystemUI/res/layout/volume_text_footer.xml54
-rw-r--r--packages/SystemUI/res/layout/volume_zen_footer.xml108
-rw-r--r--packages/SystemUI/res/layout/zen_mode_panel.xml4
-rw-r--r--packages/SystemUI/res/values/strings.xml7
-rw-r--r--packages/SystemUI/res/values/volume.xml87
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/D.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/Events.java189
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/MediaSessions.java378
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/Prefs.java69
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/SpTexts.java78
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/Util.java165
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeComponent.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java1039
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java120
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java962
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumePanelComponent.java184
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java153
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java216
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java17
60 files changed, 4773 insertions, 197 deletions
diff --git a/packages/SystemUI/res/anim/ic_volume_collapse_chevron_02_animation.xml b/packages/SystemUI/res/anim/ic_volume_collapse_chevron_02_animation.xml
new file mode 100644
index 0000000..443f2a6
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_volume_collapse_chevron_02_animation.xml
@@ -0,0 +1,25 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <objectAnimator
+ android:duration="250"
+ android:interpolator="@android:interpolator/fast_out_slow_in"
+ android:pathData="M 12.0,9.0 c 0.0,0.66667 0.0,5.0 0.0,6.0"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY" />
+
+</set> \ No newline at end of file
diff --git a/packages/SystemUI/res/anim/ic_volume_collapse_rectangle_1_animation.xml b/packages/SystemUI/res/anim/ic_volume_collapse_rectangle_1_animation.xml
new file mode 100644
index 0000000..d82f670
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_volume_collapse_rectangle_1_animation.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <objectAnimator
+ android:duration="200"
+ android:interpolator="@interpolator/ic_volume_collapse_animation_interpolator_0"
+ android:propertyName="rotation"
+ android:valueFrom="45.0"
+ android:valueTo="-45.0"
+ android:valueType="floatType" />
+
+</set> \ No newline at end of file
diff --git a/packages/SystemUI/res/anim/ic_volume_collapse_rectangle_2_animation.xml b/packages/SystemUI/res/anim/ic_volume_collapse_rectangle_2_animation.xml
new file mode 100644
index 0000000..0bc66bd
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_volume_collapse_rectangle_2_animation.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <objectAnimator
+ android:duration="200"
+ android:interpolator="@interpolator/ic_volume_collapse_animation_interpolator_0"
+ android:propertyName="rotation"
+ android:valueFrom="-45.0"
+ android:valueTo="45.0"
+ android:valueType="floatType" />
+
+</set> \ No newline at end of file
diff --git a/packages/SystemUI/res/anim/ic_volume_expand_chevron_01_animation.xml b/packages/SystemUI/res/anim/ic_volume_expand_chevron_01_animation.xml
new file mode 100644
index 0000000..e43e645
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_volume_expand_chevron_01_animation.xml
@@ -0,0 +1,25 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <objectAnimator
+ android:duration="250"
+ android:interpolator="@android:interpolator/fast_out_slow_in"
+ android:pathData="M 12.0,15.0 c 0.0,-1.0 0.0,-5.33333 0.0,-6.0"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY" />
+
+</set> \ No newline at end of file
diff --git a/packages/SystemUI/res/anim/ic_volume_expand_rectangle_3_animation.xml b/packages/SystemUI/res/anim/ic_volume_expand_rectangle_3_animation.xml
new file mode 100644
index 0000000..9b575d8
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_volume_expand_rectangle_3_animation.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <objectAnimator
+ android:duration="200"
+ android:interpolator="@interpolator/ic_volume_expand_animation_interpolator_0"
+ android:propertyName="rotation"
+ android:valueFrom="45.0"
+ android:valueTo="-45.0"
+ android:valueType="floatType" />
+
+</set> \ No newline at end of file
diff --git a/packages/SystemUI/res/anim/ic_volume_expand_rectangle_4_animation.xml b/packages/SystemUI/res/anim/ic_volume_expand_rectangle_4_animation.xml
new file mode 100644
index 0000000..6ae0fef
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_volume_expand_rectangle_4_animation.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <objectAnimator
+ android:duration="200"
+ android:interpolator="@interpolator/ic_volume_expand_animation_interpolator_0"
+ android:propertyName="rotation"
+ android:valueFrom="-45.0"
+ android:valueTo="45.0"
+ android:valueType="floatType" />
+
+</set> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/btn_borderless_rect.xml b/packages/SystemUI/res/drawable/btn_borderless_rect.xml
index d640141..c0a89f9 100644
--- a/packages/SystemUI/res/drawable/btn_borderless_rect.xml
+++ b/packages/SystemUI/res/drawable/btn_borderless_rect.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!--
+ Copyright (C) 2015 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,13 +13,15 @@
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">
+ android:color="?android:attr/colorControlHighlight" >
+
<item android:id="@android:id/mask">
<shape>
<corners android:radius="@dimen/borderless_button_radius" />
+
<solid android:color="@android:color/white" />
</shape>
</item>
+
</ripple> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_dnd.xml b/packages/SystemUI/res/drawable/ic_dnd.xml
new file mode 100644
index 0000000..17ecf21
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_dnd.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="48.0"
+ android:viewportWidth="48.0"
+ android:width="24dp" >
+
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M24.0,4.0C12.95,4.0 4.0,12.95 4.0,24.0s8.95,20.0 20.0,20.0 20.0,-8.95 20.0,-20.0S35.05,4.0 24.0,4.0zm10.0,22.0L14.0,26.0l0.0,-4.0l20.0,0.0l0.0,4.0z" />
+
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_minus.xml b/packages/SystemUI/res/drawable/ic_qs_minus.xml
index 4722c9e..6a3410a 100644
--- a/packages/SystemUI/res/drawable/ic_qs_minus.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_minus.xml
@@ -1,25 +1,26 @@
<!--
-Copyright (C) 2014 The Android Open Source Project
+ Copyright (C) 2015 The Android Open Source Project
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
+ 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
+ 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.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
+ android:height="24.0dp"
+ android:viewportHeight="48.0"
+ android:viewportWidth="48.0"
+ android:width="24.0dp" >
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M24.0,4.0C13.0,4.0 4.0,13.0 4.0,24.0s9.0,20.0 20.0,20.0c11.0,0.0 20.0,-9.0 20.0,-20.0S35.0,4.0 24.0,4.0zM34.0,26.0L14.0,26.0l0.0,-4.0l20.0,0.0L34.0,26.0z"/>
-</vector>
+ android:pathData="M38.0,26.0L10.0,26.0l0.0,-4.0l28.0,0.0l0.0,4.0z" />
+
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_qs_plus.xml b/packages/SystemUI/res/drawable/ic_qs_plus.xml
index 17d74cf..393f51c 100644
--- a/packages/SystemUI/res/drawable/ic_qs_plus.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_plus.xml
@@ -1,25 +1,26 @@
<!--
-Copyright (C) 2014 The Android Open Source Project
+ Copyright (C) 2015 The Android Open Source Project
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
+ 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
+ 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.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
+ android:height="24.0dp"
+ android:viewportHeight="48.0"
+ android:viewportWidth="48.0"
+ android:width="24.0dp" >
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M24.0,4.0C13.0,4.0 4.0,13.0 4.0,24.0s9.0,20.0 20.0,20.0c11.0,0.0 20.0,-9.0 20.0,-20.0S35.0,4.0 24.0,4.0zM34.0,26.0l-8.0,0.0l0.0,8.0l-4.0,0.0l0.0,-8.0l-8.0,0.0l0.0,-4.0l8.0,0.0l0.0,-8.0l4.0,0.0l0.0,8.0l8.0,0.0L34.0,26.0z"/>
-</vector>
+ android:pathData="M38.0,26.0L26.0,26.0l0.0,12.0l-4.0,0.0L22.0,26.0L10.0,26.0l0.0,-4.0l12.0,0.0L22.0,10.0l4.0,0.0l0.0,12.0l12.0,0.0l0.0,4.0z" />
+
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_alarm.xml b/packages/SystemUI/res/drawable/ic_volume_alarm.xml
new file mode 100644
index 0000000..a8ca0d6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_alarm.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24.0dp"
+ android:viewportHeight="48.0"
+ android:viewportWidth="48.0"
+ android:width="24.0dp" >
+
+ <path
+ android:fillColor="@color/volume_icon_color"
+ android:pathData="M44.0,11.44l-9.19,-7.71 -2.57,3.06 9.19,7.71 2.57,-3.06zm-28.24,-4.66l-2.57,-3.06 -9.19,7.71 2.57,3.06 9.19,-7.71zm9.24,9.22l-3.0,0.0l0.0,12.0l9.49,5.71 1.51,-2.47 -8.0,-4.74l0.0,-10.5zm-1.01,-8.0c-9.95,0.0 -17.99,8.06 -17.99,18.0s8.04,18.0 17.99,18.0 18.01,-8.06 18.01,-18.0 -8.06,-18.0 -18.01,-18.0zm0.01,32.0c-7.73,0.0 -14.0,-6.27 -14.0,-14.0s6.27,-14.0 14.0,-14.0 14.0,6.27 14.0,14.0 -6.26,14.0 -14.0,14.0z" />
+
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_alarm_mute.xml b/packages/SystemUI/res/drawable/ic_volume_alarm_mute.xml
new file mode 100644
index 0000000..8e2f083
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_alarm_mute.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24.0dp"
+ android:viewportHeight="48.0"
+ android:viewportWidth="48.0"
+ android:width="24.0dp" >
+
+ <path
+ android:fillColor="@color/volume_icon_color"
+ android:pathData="M24.0,12.0c7.73,0.0 14.0,6.27 14.0,14.0 0.0,1.69 -0.31,3.3 -0.86,4.8l3.04,3.04c1.16,-2.37 1.82,-5.03 1.82,-7.84 0.0,-9.94 -8.06,-18.0 -18.01,-18.0 -2.81,0.0 -5.46,0.66 -7.84,1.81l3.05,3.05c1.5,-0.55 3.11,-0.86 4.8,-0.86zm20.0,-0.56l-9.19,-7.71 -2.57,3.06 9.19,7.71 2.57,-3.06zm-38.16,-6.85l-2.55,2.54 2.66,2.66 -2.22,1.86 2.84,2.84 2.22,-1.86 1.6,1.6c-2.73,3.16 -4.39,7.27 -4.39,11.77 0.0,9.94 8.04,18.0 17.99,18.0 4.51,0.0 8.62,-1.67 11.77,-4.4l4.4,4.4 2.54,-2.55 -34.91,-34.91 -1.95,-1.95zm27.1,32.19c-2.43,2.01 -5.54,3.22 -8.94,3.22 -7.73,0.0 -14.0,-6.27 -14.0,-14.0 0.0,-3.4 1.21,-6.51 3.22,-8.94l19.72,19.72zm-16.91,-30.23l-2.84,-2.84 -1.7,1.43 2.84,2.84 1.7,-1.43z" />
+
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_bt.xml b/packages/SystemUI/res/drawable/ic_volume_bt.xml
new file mode 100644
index 0000000..bce407a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_bt.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="48.0"
+ android:viewportWidth="48.0"
+ android:width="24dp" >
+
+ <path
+ android:fillColor="@color/volume_icon_color"
+ android:pathData="M35.4,15.4L24.0,4.0l-2.0,0.0l0.0,15.2L12.8,10.0L10.0,12.8L21.2,24.0L10.0,35.2l2.8,2.8l9.2,-9.2L22.0,44.0l2.0,0.0l11.4,-11.4L26.8,24.0L35.4,15.4zM26.0,11.7l3.8,3.8L26.0,19.2L26.0,11.7zM29.8,32.6L26.0,36.3l0.0,-7.5L29.8,32.6z" />
+
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_bt_mute.xml b/packages/SystemUI/res/drawable/ic_volume_bt_mute.xml
new file mode 100644
index 0000000..98a8137
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_bt_mute.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="48.0"
+ android:viewportWidth="48.0"
+ android:width="24dp" >
+
+ <path
+ android:fillColor="@color/volume_icon_color"
+ android:pathData="M26.0,11.8l3.8,3.8l-3.2,3.2l2.8,2.8l6.0,-6.0L24.0,4.2l-2.0,0.0l0.0,10.1l4.0,4.0L26.0,11.8zM10.8,8.2L8.0,11.0l13.2,13.2L10.0,35.3l2.8,2.8L22.0,29.0l0.0,15.2l2.0,0.0l8.6,-8.6l4.6,4.6l2.8,-2.8L10.8,8.2zM26.0,36.5L26.0,29.0l3.8,3.8L26.0,36.5z" />
+
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_bt_sco.xml b/packages/SystemUI/res/drawable/ic_volume_bt_sco.xml
new file mode 100644
index 0000000..71df4d3
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_bt_sco.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24.0dp"
+ android:viewportHeight="48.0"
+ android:viewportWidth="48.0"
+ android:width="24.0dp" >
+
+ <path
+ android:fillColor="@color/volume_icon_color"
+ android:pathData="M29.41,19.0L34.0,14.41L34.0,22.0l1.0,0.0l5.71,-5.71 -4.3,-4.29 4.29,-4.29L35.0,2.0l-1.0,0.0l0.0,7.59L29.41,5.0 28.0,6.41 33.59,12.0 28.0,17.59 29.41,19.0zM36.0,5.83l1.88,1.88L36.0,9.59L36.0,5.83zm0.0,8.58l1.88,1.88L36.0,18.17l0.0,-3.76zM40.0,31.0c-2.49,0.0 -4.89,-0.4 -7.14,-1.14 -0.69,-0.22 -1.48,-0.06 -2.0,0.49l-4.4,4.41c-5.67,-2.88 -10.29,-7.51 -13.18,-13.17l4.4,-4.41c0.55,-0.5 0.71,-1.3 0.49,-2.03C17.4,12.9 17.0,10.49 17.0,8.0c0.0,-1.11 -0.89,-2.0 -2.0,-2.0L8.0,6.0c-1.11,0.0 -2.0,0.89 -2.0,2.0 0.0,18.78 15.22,34.0 34.0,34.0 1.11,0.0 2.0,-0.89 2.0,-2.0l0.0,-7.0c0.0,-1.11 -0.89,-2.0 -2.0,-2.0z" />
+
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_collapse.xml b/packages/SystemUI/res/drawable/ic_volume_collapse.xml
new file mode 100644
index 0000000..dc6d301
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_collapse.xml
@@ -0,0 +1,62 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:name="ic_volume_collapse"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+
+ <group
+ android:name="chevron_02"
+ android:rotation="90"
+ android:translateX="12"
+ android:translateY="9" >
+ <group
+ android:name="rectangle_2"
+ android:rotation="-45" >
+ <group
+ android:name="rectangle_2_pivot"
+ android:translateY="4" >
+ <group
+ android:name="rectangle_path_2_position"
+ android:translateY="-1" >
+ <path
+ android:name="rectangle_path_2"
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M -1.0,-4.0 l 2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,8.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-8.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
+ </group>
+ </group>
+ </group>
+ <group
+ android:name="rectangle_1"
+ android:rotation="45" >
+ <group
+ android:name="rectangle_1_pivot"
+ android:translateY="-4" >
+ <group
+ android:name="rectangle_path_1_position"
+ android:translateY="1" >
+ <path
+ android:name="rectangle_path_1"
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M -1.0,-4.0 l 2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,8.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-8.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
+ </group>
+ </group>
+ </group>
+ </group>
+
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_collapse_animation.xml b/packages/SystemUI/res/drawable/ic_volume_collapse_animation.xml
new file mode 100644
index 0000000..5c482bc
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_collapse_animation.xml
@@ -0,0 +1,29 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/ic_volume_collapse" >
+
+ <target
+ android:name="chevron_02"
+ android:animation="@anim/ic_volume_collapse_chevron_02_animation" />
+ <target
+ android:name="rectangle_2"
+ android:animation="@anim/ic_volume_collapse_rectangle_2_animation" />
+ <target
+ android:name="rectangle_1"
+ android:animation="@anim/ic_volume_collapse_rectangle_1_animation" />
+
+</animated-vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_expand.xml b/packages/SystemUI/res/drawable/ic_volume_expand.xml
new file mode 100644
index 0000000..a60623f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_expand.xml
@@ -0,0 +1,62 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:name="ic_volume_expand"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp" >
+
+ <group
+ android:name="chevron_01"
+ android:rotation="90"
+ android:translateX="12"
+ android:translateY="15" >
+ <group
+ android:name="rectangle_3"
+ android:rotation="45" >
+ <group
+ android:name="rectangle_2_pivot_0"
+ android:translateY="4" >
+ <group
+ android:name="rectangle_path_3_position"
+ android:translateY="-1" >
+ <path
+ android:name="rectangle_path_3"
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M -1.0,-4.0 l 2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,8.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-8.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
+ </group>
+ </group>
+ </group>
+ <group
+ android:name="rectangle_4"
+ android:rotation="-45" >
+ <group
+ android:name="rectangle_1_pivot_0"
+ android:translateY="-4" >
+ <group
+ android:name="rectangle_path_4_position"
+ android:translateY="1" >
+ <path
+ android:name="rectangle_path_4"
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M -1.0,-4.0 l 2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,8.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-8.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
+ </group>
+ </group>
+ </group>
+ </group>
+
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_expand_animation.xml b/packages/SystemUI/res/drawable/ic_volume_expand_animation.xml
new file mode 100644
index 0000000..ae2d7e4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_expand_animation.xml
@@ -0,0 +1,29 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/ic_volume_expand" >
+
+ <target
+ android:name="chevron_01"
+ android:animation="@anim/ic_volume_expand_chevron_01_animation" />
+ <target
+ android:name="rectangle_3"
+ android:animation="@anim/ic_volume_expand_rectangle_3_animation" />
+ <target
+ android:name="rectangle_4"
+ android:animation="@anim/ic_volume_expand_rectangle_4_animation" />
+
+</animated-vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_media.xml b/packages/SystemUI/res/drawable/ic_volume_media.xml
new file mode 100644
index 0000000..97089f1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_media.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24.0dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0"
+ android:width="24.0dp" >
+
+ <path
+ android:fillColor="@color/volume_icon_color"
+ android:pathData="M12.0,3.0l0.0,9.28c-0.47,-0.17 -0.97,-0.28 -1.5,-0.28C8.01,12.0 6.0,14.01 6.0,16.5S8.01,21.0 10.5,21.0c2.31,0.0 4.2,-1.75 4.45,-4.0L15.0,17.0L15.0,6.0l4.0,0.0L19.0,3.0l-7.0,0.0z" />
+
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_media_mute.xml b/packages/SystemUI/res/drawable/ic_volume_media_mute.xml
new file mode 100644
index 0000000..beb806c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_media_mute.xml
@@ -0,0 +1,29 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24.0dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0"
+ android:width="24.0dp" >
+
+ <path
+ android:fillColor="@color/volume_icon_color"
+ android:pathData="M15.0,6.0l4.0,0.0L19.0,3.0l-7.0,0.0l0.0,5.6l3.0,3.0C15.0,8.8 15.0,6.0 15.0,6.0z" />
+ <path
+ android:fillColor="@color/volume_icon_color"
+ android:pathData="M4.8,3.9L3.5,5.1l6.9,6.9C8.0,12.1 6.0,14.0 6.0,16.5C6.0,19.0 8.0,21.0 10.5,21.0c2.7,0.0 4.5,-2.3 4.5,-4.3c0.0,0.0 0.0,-0.1 0.0,-0.1l4.0,4.0l1.3,-1.3L4.8,3.9z" />
+
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_remote.xml b/packages/SystemUI/res/drawable/ic_volume_remote.xml
new file mode 100644
index 0000000..b363178
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_remote.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="48.0"
+ android:viewportWidth="48.0"
+ android:width="24dp" >
+
+ <path
+ android:fillColor="@color/volume_icon_color"
+ android:pathData="M2.0,36.0l0.0,6.0l6.0,0.0C8.0,38.7 5.3,36.0 2.0,36.0zM2.0,28.0l0.0,4.0c5.5,0.0 10.0,4.5 10.0,10.0l4.0,0.0C16.0,34.3 9.7,28.0 2.0,28.0zM38.0,14.0L10.0,14.0l0.0,3.3c7.9,2.6 14.2,8.8 16.7,16.7L38.0,34.0L38.0,14.0zM2.0,20.0l0.0,4.0c9.9,0.0 18.0,8.1 18.0,18.0l4.0,0.0C24.0,29.8 14.1,20.0 2.0,20.0zM42.0,6.0L6.0,6.0c-2.2,0.0 -4.0,1.8 -4.0,4.0l0.0,6.0l4.0,0.0l0.0,-6.0l36.0,0.0l0.0,28.0L28.0,38.0l0.0,4.0l14.0,0.0c2.2,0.0 4.0,-1.8 4.0,-4.0L46.0,10.0C46.0,7.8 44.2,6.0 42.0,6.0z" />
+
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_remote_mute.xml b/packages/SystemUI/res/drawable/ic_volume_remote_mute.xml
new file mode 100644
index 0000000..5f39ad7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_remote_mute.xml
@@ -0,0 +1,38 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24.0dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0"
+ android:width="24.0dp" >
+
+ <path
+ android:fillColor="@color/volume_icon_color"
+ android:pathData="M23.7,21.3l-1.1,-1.0c0.0,0.0 0.0,0.0 0.0,0.0L21.0,18.8l0.0,0.0L5.8,5.0l0.0,0.0L3.6,3.0l0.0,0.0L1.7,1.3L0.3,2.7l1.1,1.0C1.2,4.1 1.0,4.5 1.0,5.0l0.0,3.0l2.0,0.0L3.0,5.2L18.2,19.0L14.0,19.0l0.0,2.0l6.4,0.0l1.9,1.7L23.7,21.3z" />
+ <path
+ android:fillColor="@color/volume_icon_color"
+ android:pathData="M21.0,5.0l0.0,11.1l2.0,1.8L23.0,5.0c0.0,-1.1 -0.9,-2.0 -2.0,-2.0L6.6,3.0l2.2,2.0L21.0,5.0z" />
+ <path
+ android:fillColor="@color/volume_icon_color"
+ android:pathData="M1.0,18.0l0.0,3.0l3.0,0.0C4.0,19.3 2.7,18.0 1.0,18.0z" />
+ <path
+ android:fillColor="@color/volume_icon_color"
+ android:pathData="M1.0,14.0l0.0,2.0c2.8,0.0 5.0,2.2 5.0,5.0l2.0,0.0C8.0,17.1 4.9,14.0 1.0,14.0z" />
+ <path
+ android:fillColor="@color/volume_icon_color"
+ android:pathData="M1.0,10.0l0.0,2.0c5.0,0.0 9.0,4.0 9.0,9.0l2.0,0.0C12.0,14.9 7.1,10.0 1.0,10.0z" />
+
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_ringer.xml b/packages/SystemUI/res/drawable/ic_volume_ringer.xml
new file mode 100644
index 0000000..c566d5a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_ringer.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0"
+ android:width="24dp" >
+
+ <path
+ android:fillColor="@color/volume_icon_color"
+ android:pathData="M11.5,22.0c1.1,0.0 2.0,-0.9 2.0,-2.0l-4.0,0.0C9.5,21.1 10.4,22.0 11.5,22.0zM18.0,16.0l0.0,-5.5c0.0,-3.1 -2.1,-5.6 -5.0,-6.3L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5l0.0,0.7c-2.9,0.7 -5.0,3.2 -5.0,6.3L5.0,16.0l-2.0,2.0l0.0,1.0l17.0,0.0l0.0,-1.0L18.0,16.0z" />
+
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_ringer_mute.xml b/packages/SystemUI/res/drawable/ic_volume_ringer_mute.xml
new file mode 100644
index 0000000..0c20361
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_ringer_mute.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="48.0"
+ android:viewportWidth="48.0"
+ android:width="24dp" >
+
+ <path
+ android:fillColor="@color/volume_icon_color"
+ android:pathData="M23.000000,44.000000c2.200000,0.000000 4.000000,-1.800000 4.000000,-4.000000l-8.000000,0.000000C19.000000,42.200001 20.799999,44.000000 23.000000,44.000000zM36.000000,21.000000c0.000000,-6.100000 -4.300000,-11.300000 -10.000000,-12.600000L26.000000,7.000000c0.000000,-1.700000 -1.300000,-3.000000 -3.000000,-3.000000c-1.700000,0.000000 -3.000000,1.300000 -3.000000,3.000000l0.000000,1.400000c-1.000000,0.200000 -2.000000,0.600000 -2.900000,1.100000L36.000000,28.400000L36.000000,21.000000zM35.500000,38.000000l4.000000,4.000000l2.500000,-2.500000L8.500000,6.000000L6.000000,8.500000l5.800000,5.800000C10.700000,16.299999 10.000000,18.600000 10.000000,21.000000l0.000000,11.000000l-4.000000,4.000000l0.000000,2.000000L35.500000,38.000000z" />
+
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_ringer_vibrate.xml b/packages/SystemUI/res/drawable/ic_volume_ringer_vibrate.xml
new file mode 100644
index 0000000..38b3234
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_ringer_vibrate.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0"
+ android:width="24dp" >
+
+ <path
+ android:fillColor="@color/volume_icon_color"
+ android:pathData="M0.0,15.0l2.0,0.0L2.0,9.0L0.0,9.0L0.0,15.0zM3.0,17.0l2.0,0.0L5.0,7.0L3.0,7.0L3.0,17.0zM22.0,9.0l0.0,6.0l2.0,0.0L24.0,9.0L22.0,9.0zM19.0,17.0l2.0,0.0L21.0,7.0l-2.0,0.0L19.0,17.0zM16.5,3.0l-9.0,0.0C6.7,3.0 6.0,3.7 6.0,4.5l0.0,15.0C6.0,20.3 6.7,21.0 7.5,21.0l9.0,0.0c0.8,0.0 1.5,-0.7 1.5,-1.5l0.0,-15.0C18.0,3.7 17.3,3.0 16.5,3.0zM16.0,19.0L8.0,19.0L8.0,5.0l8.0,0.0L16.0,19.0z" />
+
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_settings.xml b/packages/SystemUI/res/drawable/ic_volume_settings.xml
new file mode 100644
index 0000000..9f79b5a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_settings.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="20dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0"
+ android:width="20dp" >
+
+ <path
+ android:fillColor="@color/volume_settings_icon_color"
+ android:pathData="M19.4,13.0c0.0,-0.3 0.1,-0.6 0.1,-1.0s0.0,-0.7 -0.1,-1.0l2.1,-1.7c0.2,-0.2 0.2,-0.4 0.1,-0.6l-2.0,-3.5C19.5,5.1 19.3,5.0 19.0,5.1l-2.5,1.0c-0.5,-0.4 -1.1,-0.7 -1.7,-1.0l-0.4,-2.6C14.5,2.2 14.2,2.0 14.0,2.0l-4.0,0.0C9.8,2.0 9.5,2.2 9.5,2.4L9.1,5.1C8.5,5.3 8.0,5.7 7.4,6.1L5.0,5.1C4.7,5.0 4.5,5.1 4.3,5.3l-2.0,3.5C2.2,8.9 2.3,9.2 2.5,9.4L4.6,11.0c0.0,0.3 -0.1,0.6 -0.1,1.0s0.0,0.7 0.1,1.0l-2.1,1.7c-0.2,0.2 -0.2,0.4 -0.1,0.6l2.0,3.5C4.5,18.9 4.7,19.0 5.0,18.9l2.5,-1.0c0.5,0.4 1.1,0.7 1.7,1.0l0.4,2.6c0.0,0.2 0.2,0.4 0.5,0.4l4.0,0.0c0.2,0.0 0.5,-0.2 0.5,-0.4l0.4,-2.6c0.6,-0.3 1.2,-0.6 1.7,-1.0l2.5,1.0c0.2,0.1 0.5,0.0 0.6,-0.2l2.0,-3.5c0.1,-0.2 0.1,-0.5 -0.1,-0.6L19.4,13.0zM12.0,15.5c-1.9,0.0 -3.5,-1.6 -3.5,-3.5s1.6,-3.5 3.5,-3.5s3.5,1.6 3.5,3.5S13.9,15.5 12.0,15.5z" />
+
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_system.xml b/packages/SystemUI/res/drawable/ic_volume_system.xml
new file mode 100644
index 0000000..ccd8e18
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_system.xml
@@ -0,0 +1,32 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24.0dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0"
+ android:width="24.0dp" >
+
+ <path
+ android:fillColor="@color/volume_icon_color"
+ android:pathData="M17.7,14.8l-4.5,-2.3c-0.2,-0.1 -0.4,-0.1 -0.5,-0.1l-0.8,0.0l0.0,-6.0c0.0,-0.8 -0.7,-1.5 -1.5,-1.5S8.9,5.6 8.9,6.4l0.0,10.7l-3.4,-0.7c-0.1,0.0 -0.2,0.0 -0.2,0.0c-0.3,0.0 -0.6,0.1 -0.8,0.3l-0.8,0.8l4.9,4.9c0.3,0.3 0.6,0.4 1.1,0.4l6.8,0.0c0.8,0.0 1.3,-0.6 1.4,-1.3l0.8,-5.3c0.0,-0.1 0.0,-0.1 0.0,-0.2C18.6,15.5 18.2,15.0 17.7,14.8z" />
+ <path
+ android:fillColor="@color/volume_icon_color"
+ android:pathData="M14.3,8.8l1.8,0.9c1.5,-2.0 1.4,-4.8 -0.4,-6.6l-1.4,1.4C15.5,5.7 15.5,7.6 14.3,8.8z" />
+ <path
+ android:fillColor="@color/volume_icon_color"
+ android:pathData="M17.9,10.6l1.8,0.9C22.0,8.0 21.6,3.3 18.5,0.3l-1.4,1.4C19.5,4.1 19.8,7.9 17.9,10.6z" />
+
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_system_mute.xml b/packages/SystemUI/res/drawable/ic_volume_system_mute.xml
new file mode 100644
index 0000000..dfcb655
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_system_mute.xml
@@ -0,0 +1,35 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24.0dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0"
+ android:width="24.0dp" >
+
+ <path
+ android:fillColor="@color/volume_icon_color"
+ android:pathData="M14.3,8.8l1.8,0.9c1.5,-2.0 1.4,-4.8 -0.4,-6.6l-1.4,1.4C15.5,5.7 15.5,7.6 14.3,8.8z" />
+ <path
+ android:fillColor="@color/volume_icon_color"
+ android:pathData="M17.9,10.6l1.8,0.9C22.0,8.0 21.6,3.3 18.5,0.3l-1.4,1.4C19.5,4.1 19.8,7.9 17.9,10.6z" />
+ <path
+ android:fillColor="@color/volume_icon_color"
+ android:pathData="M22.0,20.6l-3.5,-2.8l0.0,0.0l-9.6,-7.6l0.0,0.0L3.2,5.7L2.0,7.3l6.9,5.4L8.9,17.0l-3.4,-0.7c-0.1,0.0 -0.2,0.0 -0.2,0.0c-0.3,0.0 -0.6,0.1 -0.8,0.3l-0.8,0.8l4.9,4.9c0.3,0.3 0.6,0.4 1.1,0.4l6.8,0.0c0.8,0.0 1.3,-0.6 1.4,-1.3l0.2,-1.5l2.6,2.1L22.0,20.6z" />
+ <path
+ android:fillColor="@color/volume_icon_color"
+ android:pathData="M11.9,6.4c0.0,-0.8 -0.7,-1.5 -1.5,-1.5S8.9,5.6 8.9,6.4l0.0,1.5l3.0,2.4L11.9,6.4z" />
+
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_voice.xml b/packages/SystemUI/res/drawable/ic_volume_voice.xml
new file mode 100644
index 0000000..133253e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_voice.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24.0dp"
+ android:viewportHeight="48.0"
+ android:viewportWidth="48.0"
+ android:width="24.0dp" >
+
+ <path
+ android:fillColor="@color/volume_icon_color"
+ android:pathData="M13.25,21.59c2.88,5.66 7.51,10.29 13.18,13.17l4.4,-4.41c0.55,-0.55 1.34,-0.71 2.03,-0.49C35.1,30.6 37.51,31.0 40.0,31.0c1.11,0.0 2.0,0.89 2.0,2.0l0.0,7.0c0.0,1.11 -0.89,2.0 -2.0,2.0C21.22,42.0 6.0,26.78 6.0,8.0c0.0,-1.1 0.9,-2.0 2.0,-2.0l7.0,0.0c1.11,0.0 2.0,0.89 2.0,2.0 0.0,2.4 0.4,4.9 1.14,7.1 0.2,0.6 0.06,1.48 -0.49,2.03l-4.4,4.42z" />
+
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/volume_dialog_background.xml b/packages/SystemUI/res/drawable/volume_dialog_background.xml
new file mode 100644
index 0000000..f09c01b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/volume_dialog_background.xml
@@ -0,0 +1,22 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <solid android:color="@color/system_primary_color" />
+
+ <corners android:radius="@dimen/notification_material_rounded_rect_radius" />
+
+</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/interpolator/ic_volume_collapse_animation_interpolator_0.xml b/packages/SystemUI/res/interpolator/ic_volume_collapse_animation_interpolator_0.xml
new file mode 100644
index 0000000..c3930e4
--- /dev/null
+++ b/packages/SystemUI/res/interpolator/ic_volume_collapse_animation_interpolator_0.xml
@@ -0,0 +1,17 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:pathData="M 0.0,0.0 c 0.0001,0.0 0.0,1.0 1.0,1.0" />
diff --git a/packages/SystemUI/res/interpolator/ic_volume_expand_animation_interpolator_0.xml b/packages/SystemUI/res/interpolator/ic_volume_expand_animation_interpolator_0.xml
new file mode 100644
index 0000000..c3930e4
--- /dev/null
+++ b/packages/SystemUI/res/interpolator/ic_volume_expand_animation_interpolator_0.xml
@@ -0,0 +1,17 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:pathData="M 0.0,0.0 c 0.0001,0.0 0.0,1.0 1.0,1.0" />
diff --git a/packages/SystemUI/res/layout/segmented_button.xml b/packages/SystemUI/res/layout/segmented_button.xml
index e92f310..2f84bab 100644
--- a/packages/SystemUI/res/layout/segmented_button.xml
+++ b/packages/SystemUI/res/layout/segmented_button.xml
@@ -19,10 +19,8 @@
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/segmented_button_spacing"
android:layout_weight="1"
- android:gravity="center_horizontal|top"
+ android:gravity="center"
android:textColor="@color/segmented_button_text_selector"
android:background="@drawable/btn_borderless_rect"
- android:textAppearance="@style/TextAppearance.QS.SegmentedButton"
- android:minHeight="64dp"
- android:paddingTop="11dp"
- android:drawablePadding="6dp" />
+ android:textAppearance="@style/TextAppearance.Volume.ZenSwitchSummary"
+ android:minHeight="48dp" />
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 3765fe8..7586227 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ Copyright (C) 2015 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -14,15 +13,50 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/volume_dialog"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp"
android:layout_marginLeft="@dimen/notification_side_padding"
android:layout_marginRight="@dimen/notification_side_padding"
- android:background="@drawable/qs_background_primary"
- android:translationZ="@dimen/volume_panel_z"
- android:layout_marginBottom="@dimen/volume_panel_z">
+ android:layout_marginTop="4dp"
+ android:background="@drawable/volume_dialog_background"
+ android:translationZ="4dp" >
- <include layout="@layout/volume_panel" />
+ <com.android.keyguard.AlphaOptimizedImageButton
+ android:id="@+id/volume_expand_button"
+ style="@style/VolumeButtons"
+ android:layout_alignParentLeft="true"
+ android:layout_width="@dimen/volume_button_size"
+ android:layout_height="@dimen/volume_button_size"
+ android:clickable="true"
+ android:soundEffectsEnabled="false"
+ android:src="@drawable/ic_volume_collapse_animation" />
-</FrameLayout> \ No newline at end of file
+ <LinearLayout
+ android:id="@+id/volume_dialog_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingBottom="4dp"
+ android:paddingEnd="4dp"
+ android:paddingStart="4dp"
+ android:paddingTop="6dp" >
+
+ <!-- volume rows added and removed here! :-) -->
+
+ <FrameLayout
+ android:id="@+id/volume_footer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ tools:ignore="UselessParent" >
+
+ <include layout="@layout/volume_text_footer" />
+
+ <include layout="@layout/volume_zen_footer" />
+ </FrameLayout>
+ </LinearLayout>
+
+</RelativeLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml
new file mode 100644
index 0000000..af27cc4
--- /dev/null
+++ b/packages/SystemUI/res/layout/volume_dialog_row.xml
@@ -0,0 +1,64 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false" >
+
+ <TextView
+ android:id="@+id/volume_row_header"
+ style="?android:attr/textAppearanceButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:alpha="@dimen/volume_secondary_alpha"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:paddingBottom="0dp"
+ android:paddingEnd="12dp"
+ android:paddingStart="13dp"
+ android:paddingTop="8dp" />
+
+ <com.android.keyguard.AlphaOptimizedImageButton
+ android:id="@+id/volume_row_icon"
+ style="@style/VolumeButtons"
+ android:layout_width="@dimen/volume_button_size"
+ android:layout_height="@dimen/volume_button_size"
+ android:layout_below="@id/volume_row_header"
+ android:soundEffectsEnabled="false" />
+
+ <SeekBar
+ android:id="@+id/volume_row_slider"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBottom="@+id/volume_row_icon"
+ android:layout_alignWithParentIfMissing="true"
+ android:layout_below="@id/volume_row_header"
+ android:layout_toEndOf="@id/volume_row_icon"
+ android:layout_toStartOf="@+id/volume_settings_button"
+ android:paddingEnd="4dp"
+ android:paddingStart="4dp"
+ android:progressTint="@android:color/white"
+ android:thumbTint="@android:color/white" />
+
+ <com.android.keyguard.AlphaOptimizedImageButton
+ android:id="@+id/volume_settings_button"
+ style="@style/VolumeButtons"
+ android:layout_width="@dimen/volume_button_size"
+ android:layout_height="@dimen/volume_button_size"
+ android:layout_alignParentEnd="true"
+ android:layout_below="@id/volume_row_header" />
+
+</RelativeLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_panel_dialog.xml b/packages/SystemUI/res/layout/volume_panel_dialog.xml
new file mode 100644
index 0000000..700102f
--- /dev/null
+++ b/packages/SystemUI/res/layout/volume_panel_dialog.xml
@@ -0,0 +1,27 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/volume_panel_z"
+ android:layout_marginLeft="@dimen/notification_side_padding"
+ android:layout_marginRight="@dimen/notification_side_padding"
+ android:background="@drawable/qs_background_primary"
+ android:translationZ="@dimen/volume_panel_z" >
+
+ <include layout="@layout/volume_panel" />
+
+</FrameLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_text_footer.xml b/packages/SystemUI/res/layout/volume_text_footer.xml
new file mode 100644
index 0000000..7436488
--- /dev/null
+++ b/packages/SystemUI/res/layout/volume_text_footer.xml
@@ -0,0 +1,54 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/volume_text_footer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ tools:ignore="UselessParent" >
+
+ <TextView
+ android:id="@+id/volume_footline_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBaseline="@+id/volume_footline_action_button"
+ android:alpha="@dimen/volume_secondary_alpha"
+ android:fontFamily="sans-serif"
+ android:paddingEnd="8dp"
+ android:paddingStart="13dp"
+ android:textColor="?android:attr/textColorPrimary" />
+
+ <Button
+ android:id="@+id/volume_footline_action_button"
+ style="@android:style/Widget.Material.Button.Borderless"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/volume_button_size"
+ android:layout_toEndOf="@id/volume_footline_text"
+ android:layout_toStartOf="@+id/volume_settings_button"
+ android:alpha="@dimen/volume_secondary_alpha"
+ android:paddingEnd="0dp"
+ android:paddingStart="0dp" />
+
+ <com.android.keyguard.AlphaOptimizedImageButton
+ android:id="@+id/volume_settings_button"
+ style="@style/VolumeButtons"
+ android:layout_width="@dimen/volume_button_size"
+ android:layout_height="@dimen/volume_button_size"
+ android:layout_alignParentEnd="true"
+ android:src="@drawable/ic_volume_settings" />
+
+</RelativeLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_zen_footer.xml b/packages/SystemUI/res/layout/volume_zen_footer.xml
new file mode 100644
index 0000000..f4a8b89
--- /dev/null
+++ b/packages/SystemUI/res/layout/volume_zen_footer.xml
@@ -0,0 +1,108 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.systemui.volume.ZenFooter xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/volume_zen_footer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" > <!-- extends LinearLayout -->
+
+ <LinearLayout
+ android:id="@+id/volume_zen_switch_bar"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/volume_button_size"
+ android:clickable="true"
+ android:orientation="horizontal" >
+
+ <ImageView
+ android:id="@+id/volume_zen_switch_bar_icon"
+ android:layout_width="@dimen/volume_button_size"
+ android:layout_height="@dimen/volume_button_size"
+ android:scaleType="center"
+ android:src="@drawable/ic_dnd" />
+
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"
+ android:gravity="center_vertical"
+ android:textDirection="locale"
+ android:padding="3dp"
+ android:text="@string/volume_zen_switch_text"
+ android:textAppearance="@style/TextAppearance.Volume.ZenSwitch" />
+
+ <Switch
+ android:id="@+id/volume_zen_switch"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_marginEnd="11dp" />
+ </LinearLayout>
+
+ <RelativeLayout
+ android:id="@+id/volume_zen_panel_summary"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/volume_button_size"
+ android:layout_marginStart="@dimen/volume_button_size"
+ android:paddingEnd="3dp"
+ android:paddingStart="3dp" >
+
+ <TextView
+ android:id="@+id/volume_zen_panel_summary_line_1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.Volume.ZenSwitchSummary" />
+
+ <TextView
+ android:id="@+id/volume_zen_panel_summary_line_2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/volume_zen_panel_summary_line_1"
+ android:textAppearance="@style/TextAppearance.Volume.ZenSwitchDetail" />
+ </RelativeLayout>
+
+ <include layout="@layout/zen_mode_panel" />
+
+ <LinearLayout
+ android:id="@+id/volume_zen_mode_panel_buttons"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="end" >
+
+ <TextView
+ android:id="@+id/volume_zen_mode_panel_more"
+ style="@style/QSBorderlessButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="8dp"
+ android:clickable="true"
+ android:focusable="true"
+ android:minWidth="132dp"
+ android:text="@string/quick_settings_more_settings"
+ android:textAppearance="@style/TextAppearance.QS.DetailButton" />
+
+ <TextView
+ android:id="@+id/volume_zen_mode_panel_done"
+ style="@style/QSBorderlessButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clickable="true"
+ android:focusable="true"
+ android:minWidth="84dp"
+ android:text="@string/quick_settings_done"
+ android:textAppearance="@style/TextAppearance.QS.DetailButton" />
+ </LinearLayout>
+
+</com.android.systemui.volume.ZenFooter> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/zen_mode_panel.xml b/packages/SystemUI/res/layout/zen_mode_panel.xml
index 33c1899..ef403e4 100644
--- a/packages/SystemUI/res/layout/zen_mode_panel.xml
+++ b/packages/SystemUI/res/layout/zen_mode_panel.xml
@@ -34,8 +34,8 @@
android:id="@+id/zen_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginLeft="8dp"
- android:layout_marginRight="8dp"
+ android:layout_marginLeft="40dp"
+ android:layout_marginRight="40dp"
android:layout_marginBottom="8dp"
android:clipChildren="false" />
</FrameLayout>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 8bfa65d..1c8142f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -753,10 +753,10 @@
<string name="camera_hint">Swipe left for camera</string>
<!-- Interruption level: None. [CHAR LIMIT=20] -->
- <string name="interruption_level_none">None</string>
+ <string name="interruption_level_none">No interruptions</string>
<!-- Interruption level: Priority. [CHAR LIMIT=20] -->
- <string name="interruption_level_priority">Priority</string>
+ <string name="interruption_level_priority">Priority only</string>
<!-- Interruption level: All. [CHAR LIMIT=20] -->
<string name="interruption_level_all">All</string>
@@ -960,4 +960,7 @@
<!-- VolumeUI restoration notification: text -->
<string name="volumeui_notification_text">Touch to restore the original.</string>
+
+ <!-- Volume dialog zen toggle switch title -->
+ <string name="volume_zen_switch_text">Block interruptions</string>
</resources>
diff --git a/packages/SystemUI/res/values/volume.xml b/packages/SystemUI/res/values/volume.xml
new file mode 100644
index 0000000..f516104
--- /dev/null
+++ b/packages/SystemUI/res/values/volume.xml
@@ -0,0 +1,87 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item name="volume_expand_animation_duration" type="integer">300</item>
+
+ <color name="volume_icon_color">#ffffffff</color>
+ <color name="volume_settings_icon_color">#7fffffff</color>
+
+ <dimen name="volume_slider_interspacing">2dp</dimen>
+ <dimen name="volume_offset_top">0dp</dimen>
+ <dimen name="volume_button_size">48dp</dimen>
+
+ <item name="volume_secondary_alpha" format="float" type="dimen">0.3</item>
+
+ <style name="VolumeDialogAnimations">
+ <item name="android:windowEnterAnimation">@android:anim/fade_in</item>
+ <item name="android:windowExitAnimation">@android:anim/fade_out</item>
+ </style>
+
+ <style name="VolumeButtons" parent="@android:style/Widget.Material.Button.Borderless">
+ <item name="android:background">@drawable/btn_borderless_rect</item>
+ </style>
+
+ <style name="TextAppearance" />
+
+ <style name="TextAppearance.Volume">
+ <item name="android:textStyle">normal</item>
+ <item name="android:textColor">#ffffffff</item>
+ <item name="android:fontFamily">sans-serif</item>
+ </style>
+
+ <style name="TextAppearance.Volume.ZenSwitch">
+ <item name="android:textSize">16sp</item>
+ <item name="android:fontFamily">sans-serif-medium</item>
+ </style>
+
+ <style name="TextAppearance.Volume.ZenSwitchSummary">
+ <item name="android:textSize">14sp</item>
+ <item name="android:fontFamily">sans-serif-medium</item>
+ </style>
+
+ <style name="TextAppearance.Volume.ZenSwitchDetail">
+ <item name="android:textSize">14sp</item>
+ <item name="android:fontFamily">sans-serif</item>
+ <item name="android:textColor">#ffb0b3c5</item>
+ </style>
+
+ <string-array name="volume_stream_titles" translatable="false">
+ <item>Voice calls</item> <!-- STREAM_VOICE_CALL -->
+ <item>System</item> <!-- STREAM_SYSTEM -->
+ <item>Notifications</item> <!-- STREAM_RING -->
+ <item>Media</item> <!-- STREAM_MUSIC -->
+ <item>Alarms</item> <!-- STREAM_ALARM -->
+ <item></item> <!-- STREAM_NOTIFICATION -->
+ <item>Bluetooth calls</item> <!-- STREAM_BLUETOOTH_SCO -->
+ <item></item> <!-- STREAM_SYSTEM_ENFORCED -->
+ <item></item> <!-- STREAM_DTMF -->
+ <item></item> <!-- STREAM_TTS -->
+ </string-array>
+
+ <string name="volume_dnd_is_on" translatable="false">Do not disturb is on</string>
+ <string name="volume_turn_off" translatable="false">Turn off</string>
+ <string name="volume_stream_muted" translatable="false">%s silent</string>
+ <string name="volume_stream_vibrate" translatable="false">%s vibrate</string>
+ <string name="volume_stream_suppressed" translatable="false">%1$s silent — %2$s</string>
+ <string name="volume_stream_muted_dnd" translatable="false">%s silent — No interruptions</string>
+ <string name="volume_stream_limited_dnd" translatable="false">%s — Priority only</string>
+ <string name="volume_stream_vibrate_dnd" translatable="false">%s vibrate — Priority only</string>
+ <string name="volume_dnd_ends_in" translatable="false">Do not disturb ends in %s</string>
+ <string name="volume_dnd_ends_at" translatable="false">Do not disturb ends at %s</string>
+ <string name="volume_end_now" translatable="false">End now</string>
+
+</resources> \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 79600f5..182a2dd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -40,6 +40,7 @@ public class DndTile extends QSTile<QSTile.BooleanState> {
private static final String ACTION_SET_VISIBLE = "com.android.systemui.dndtile.SET_VISIBLE";
private static final String EXTRA_VISIBLE = "visible";
private static final String PREF_KEY_VISIBLE = "DndTileVisible";
+ private static final String PREF_KEY_COMBINED_ICON = "DndTileCombinedIcon";
private final ZenModeController mController;
private final DndDetailAdapter mDetailAdapter;
@@ -52,7 +53,7 @@ public class DndTile extends QSTile<QSTile.BooleanState> {
super(host);
mController = host.getZenModeController();
mDetailAdapter = new DndDetailAdapter();
- mVisible = getSharedPrefs(mContext).getBoolean(PREF_KEY_VISIBLE, false);
+ mVisible = isVisible(host.getContext());
mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_SET_VISIBLE));
}
@@ -65,6 +66,14 @@ public class DndTile extends QSTile<QSTile.BooleanState> {
return getSharedPrefs(context).getBoolean(PREF_KEY_VISIBLE, false);
}
+ public static void setCombinedIcon(Context context, boolean combined) {
+ getSharedPrefs(context).edit().putBoolean(PREF_KEY_COMBINED_ICON, combined).commit();
+ }
+
+ public static boolean isCombinedIcon(Context context) {
+ return getSharedPrefs(context).getBoolean(PREF_KEY_COMBINED_ICON, false);
+ }
+
@Override
public DetailAdapter getDetailAdapter() {
return mDetailAdapter;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 2236aae..1938c6d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -199,7 +199,7 @@ public class PhoneStatusBarPolicy {
int volumeIconId = 0;
String volumeDescription = null;
- if (DndTile.isVisible(mContext)) {
+ if (DndTile.isVisible(mContext) || DndTile.isCombinedIcon(mContext)) {
zenVisible = mZen != Global.ZEN_MODE_OFF;
zenIconId = R.drawable.stat_sys_dnd;
zenDescription = mContext.getString(R.string.quick_settings_dnd_label);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/D.java b/packages/SystemUI/src/com/android/systemui/volume/D.java
new file mode 100644
index 0000000..db7c853
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/D.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume;
+
+import android.util.Log;
+
+class D {
+ public static boolean BUG = Log.isLoggable("volume", Log.DEBUG);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/Events.java b/packages/SystemUI/src/com/android/systemui/volume/Events.java
new file mode 100644
index 0000000..b20e39c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/Events.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume;
+
+import android.media.AudioManager;
+import android.media.AudioSystem;
+import android.provider.Settings.Global;
+import android.util.Log;
+
+import com.android.systemui.volume.VolumeDialogController.State;
+
+import java.util.Arrays;
+
+/**
+ * Interesting events related to the volume.
+ */
+public class Events {
+ private static final String TAG = Util.logTag(Events.class);
+
+ public static final int EVENT_SHOW_DIALOG = 0; // (reason|int) (keyguard|bool)
+ public static final int EVENT_DISMISS_DIALOG = 1; // (reason|int)
+ public static final int EVENT_ACTIVE_STREAM_CHANGED = 2; // (stream|int)
+ public static final int EVENT_EXPAND = 3; // (expand|bool)
+ public static final int EVENT_KEY = 4;
+ public static final int EVENT_COLLECTION_STARTED = 5;
+ public static final int EVENT_COLLECTION_STOPPED = 6;
+ public static final int EVENT_ICON_CLICK = 7; // (stream|int) (icon_state|int)
+ public static final int EVENT_SETTINGS_CLICK = 8;
+ public static final int EVENT_TOUCH_LEVEL_CHANGED = 9; // (stream|int) (level|int)
+ public static final int EVENT_LEVEL_CHANGED = 10; // (stream|int) (level|int)
+ public static final int EVENT_INTERNAL_RINGER_MODE_CHANGED = 11; // (mode|int)
+ public static final int EVENT_EXTERNAL_RINGER_MODE_CHANGED = 12; // (mode|int)
+ public static final int EVENT_ZEN_MODE_CHANGED = 13; // (mode|int)
+ public static final int EVENT_SUPPRESSOR_CHANGED = 14; // (component|string) (name|string)
+ public static final int EVENT_MUTE_CHANGED = 15; // (stream|int) (muted|bool)
+
+ private static final String[] EVENT_TAGS = {
+ "show_dialog",
+ "dismiss_dialog",
+ "active_stream_changed",
+ "expand",
+ "key",
+ "collection_started",
+ "collection_stopped",
+ "icon_click",
+ "settings_click",
+ "touch_level_changed",
+ "level_changed",
+ "internal_ringer_mode_changed",
+ "external_ringer_mode_changed",
+ "zen_mode_changed",
+ "suppressor_changed",
+ "mute_changed",
+ };
+
+ public static final int DISMISS_REASON_UNKNOWN = 0;
+ public static final int DISMISS_REASON_TOUCH_OUTSIDE = 1;
+ public static final int DISMISS_REASON_VOLUME_CONTROLLER = 2;
+ public static final int DISMISS_REASON_TIMEOUT = 3;
+ public static final int DISMISS_REASON_SCREEN_OFF = 4;
+ public static final int DISMISS_REASON_SETTINGS_CLICKED = 5;
+ public static final int DISMISS_REASON_DONE_CLICKED = 6;
+ public static final String[] DISMISS_REASONS = {
+ "unknown",
+ "touch_outside",
+ "volume_controller",
+ "timeout",
+ "screen_off",
+ "settings_clicked",
+ "done_clicked",
+ };
+
+ public static final int SHOW_REASON_UNKNOWN = 0;
+ public static final int SHOW_REASON_VOLUME_CHANGED = 1;
+ public static final int SHOW_REASON_REMOTE_VOLUME_CHANGED = 2;
+ public static final String[] SHOW_REASONS = {
+ "unknown",
+ "volume_changed",
+ "remote_volume_changed"
+ };
+
+ public static final int ICON_STATE_UNKNOWN = 0;
+ public static final int ICON_STATE_UNMUTE = 1;
+ public static final int ICON_STATE_MUTE = 2;
+ public static final int ICON_STATE_VIBRATE = 3;
+
+ public static Callback sCallback;
+
+ public static void writeEvent(int tag, Object... list) {
+ final long time = System.currentTimeMillis();
+ final StringBuilder sb = new StringBuilder("writeEvent ").append(EVENT_TAGS[tag]);
+ if (list != null && list.length > 0) {
+ sb.append(" ");
+ switch (tag) {
+ case EVENT_SHOW_DIALOG:
+ sb.append(SHOW_REASONS[(Integer) list[0]]).append(" keyguard=").append(list[1]);
+ break;
+ case EVENT_EXPAND:
+ sb.append(list[0]);
+ break;
+ case EVENT_DISMISS_DIALOG:
+ sb.append(DISMISS_REASONS[(Integer) list[0]]);
+ break;
+ case EVENT_ACTIVE_STREAM_CHANGED:
+ sb.append(AudioSystem.streamToString((Integer) list[0]));
+ break;
+ case EVENT_ICON_CLICK:
+ sb.append(AudioSystem.streamToString((Integer) list[0])).append(' ')
+ .append(iconStateToString((Integer) list[1]));
+ break;
+ case EVENT_TOUCH_LEVEL_CHANGED:
+ case EVENT_LEVEL_CHANGED:
+ case EVENT_MUTE_CHANGED:
+ sb.append(AudioSystem.streamToString((Integer) list[0])).append(' ')
+ .append(list[1]);
+ break;
+ case EVENT_INTERNAL_RINGER_MODE_CHANGED:
+ case EVENT_EXTERNAL_RINGER_MODE_CHANGED:
+ sb.append(ringerModeToString((Integer) list[0]));
+ break;
+ case EVENT_ZEN_MODE_CHANGED:
+ sb.append(zenModeToString((Integer) list[0]));
+ break;
+ case EVENT_SUPPRESSOR_CHANGED:
+ sb.append(list[0]).append(' ').append(list[1]);
+ break;
+ default:
+ sb.append(Arrays.asList(list));
+ break;
+ }
+ }
+ Log.i(TAG, sb.toString());
+ if (sCallback != null) {
+ sCallback.writeEvent(time, tag, list);
+ }
+ }
+
+ public static void writeState(long time, State state) {
+ if (sCallback != null) {
+ sCallback.writeState(time, state);
+ }
+ }
+
+ private static String iconStateToString(int iconState) {
+ switch (iconState) {
+ case ICON_STATE_UNMUTE: return "unmute";
+ case ICON_STATE_MUTE: return "mute";
+ case ICON_STATE_VIBRATE: return "vibrate";
+ default: return "unknown_state_" + iconState;
+ }
+ }
+
+ private static String ringerModeToString(int ringerMode) {
+ switch (ringerMode) {
+ case AudioManager.RINGER_MODE_SILENT: return "silent";
+ case AudioManager.RINGER_MODE_VIBRATE: return "vibrate";
+ case AudioManager.RINGER_MODE_NORMAL: return "normal";
+ default: return "unknown";
+ }
+ }
+
+ private static String zenModeToString(int zenMode) {
+ switch (zenMode) {
+ case Global.ZEN_MODE_OFF: return "off";
+ case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: return "important_interruptions";
+ case Global.ZEN_MODE_NO_INTERRUPTIONS: return "no_interruptions";
+ default: return "unknown";
+ }
+ }
+
+ public interface Callback {
+ void writeEvent(long time, int tag, Object[] list);
+ void writeState(long time, State state);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/MediaSessions.java b/packages/SystemUI/src/com/android/systemui/volume/MediaSessions.java
new file mode 100644
index 0000000..712ea27
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/MediaSessions.java
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.media.IRemoteVolumeController;
+import android.media.MediaMetadata;
+import android.media.session.ISessionController;
+import android.media.session.MediaController;
+import android.media.session.MediaController.PlaybackInfo;
+import android.media.session.MediaSession.QueueItem;
+import android.media.session.MediaSession.Token;
+import android.media.session.MediaSessionManager;
+import android.media.session.MediaSessionManager.OnActiveSessionsChangedListener;
+import android.media.session.PlaybackState;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Convenience client for all media session updates. Provides a callback interface for events
+ * related to remote media sessions.
+ */
+public class MediaSessions {
+ private static final String TAG = Util.logTag(MediaSessions.class);
+
+ private static final boolean USE_SERVICE_LABEL = false;
+
+ private final Context mContext;
+ private final H mHandler;
+ private final MediaSessionManager mMgr;
+ private final Map<Token, MediaControllerRecord> mRecords = new HashMap<>();
+ private final Callbacks mCallbacks;
+
+ private boolean mInit;
+
+ public MediaSessions(Context context, Looper looper, Callbacks callbacks) {
+ mContext = context;
+ mHandler = new H(looper);
+ mMgr = (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE);
+ mCallbacks = callbacks;
+ }
+
+ public void dump(PrintWriter writer) {
+ writer.println(getClass().getSimpleName() + " state:");
+ writer.print(" mInit: "); writer.println(mInit);
+ writer.print(" mRecords.size: "); writer.println(mRecords.size());
+ int i = 0;
+ for (MediaControllerRecord r : mRecords.values()) {
+ dump(++i, writer, r.controller);
+ }
+ }
+
+ public void init() {
+ if (D.BUG) Log.d(TAG, "init");
+ // will throw if no permission
+ mMgr.addOnActiveSessionsChangedListener(mSessionsListener, null, mHandler);
+ mInit = true;
+ postUpdateSessions();
+ mMgr.setRemoteVolumeController(mRvc);
+ }
+
+ protected void postUpdateSessions() {
+ if (!mInit) return;
+ mHandler.sendEmptyMessage(H.UPDATE_SESSIONS);
+ }
+
+ public void destroy() {
+ if (D.BUG) Log.d(TAG, "destroy");
+ mInit = false;
+ mMgr.removeOnActiveSessionsChangedListener(mSessionsListener);
+ }
+
+ public void setVolume(Token token, int level) {
+ final MediaControllerRecord r = mRecords.get(token);
+ if (r == null) {
+ Log.w(TAG, "setVolume: No record found for token " + token);
+ return;
+ }
+ if (D.BUG) Log.d(TAG, "Setting level to " + level);
+ r.controller.setVolumeTo(level, 0);
+ }
+
+ private void onRemoteVolumeChangedH(ISessionController session, int flags) {
+ final MediaController controller = new MediaController(mContext, session);
+ if (D.BUG) Log.d(TAG, "remoteVolumeChangedH " + controller.getPackageName() + " "
+ + Util.audioManagerFlagsToString(flags));
+ final Token token = controller.getSessionToken();
+ mCallbacks.onRemoteVolumeChanged(token, flags);
+ }
+
+ private void onUpdateRemoteControllerH(ISessionController session) {
+ final MediaController controller = session != null ? new MediaController(mContext, session)
+ : null;
+ final String pkg = controller != null ? controller.getPackageName() : null;
+ if (D.BUG) Log.d(TAG, "updateRemoteControllerH " + pkg);
+ // this may be our only indication that a remote session is changed, refresh
+ postUpdateSessions();
+ }
+
+ protected void onActiveSessionsUpdatedH(List<MediaController> controllers) {
+ if (D.BUG) Log.d(TAG, "onActiveSessionsUpdatedH n=" + controllers.size());
+ final Set<Token> toRemove = new HashSet<Token>(mRecords.keySet());
+ for (MediaController controller : controllers) {
+ final Token token = controller.getSessionToken();
+ final PlaybackInfo pi = controller.getPlaybackInfo();
+ toRemove.remove(token);
+ if (!mRecords.containsKey(token)) {
+ final MediaControllerRecord r = new MediaControllerRecord(controller);
+ r.name = getControllerName(controller);
+ mRecords.put(token, r);
+ controller.registerCallback(r, mHandler);
+ }
+ final MediaControllerRecord r = mRecords.get(token);
+ final boolean remote = isRemote(pi);
+ if (remote) {
+ updateRemoteH(token, r.name, pi);
+ r.sentRemote = true;
+ }
+ }
+ for (Token t : toRemove) {
+ final MediaControllerRecord r = mRecords.get(t);
+ r.controller.unregisterCallback(r);
+ mRecords.remove(t);
+ if (D.BUG) Log.d(TAG, "Removing " + r.name + " sentRemote=" + r.sentRemote);
+ if (r.sentRemote) {
+ mCallbacks.onRemoteRemoved(t);
+ r.sentRemote = false;
+ }
+ }
+ }
+
+ private static boolean isRemote(PlaybackInfo pi) {
+ return pi != null && pi.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE;
+ }
+
+ protected String getControllerName(MediaController controller) {
+ final PackageManager pm = mContext.getPackageManager();
+ final String pkg = controller.getPackageName();
+ try {
+ if (USE_SERVICE_LABEL) {
+ final List<ResolveInfo> ris = pm.queryIntentServices(
+ new Intent("android.media.MediaRouteProviderService").setPackage(pkg), 0);
+ if (ris != null) {
+ for (ResolveInfo ri : ris) {
+ if (ri.serviceInfo == null) continue;
+ if (pkg.equals(ri.serviceInfo.packageName)) {
+ final String serviceLabel =
+ Objects.toString(ri.serviceInfo.loadLabel(pm), "").trim();
+ if (serviceLabel.length() > 0) {
+ return serviceLabel;
+ }
+ }
+ }
+ }
+ }
+ final ApplicationInfo ai = pm.getApplicationInfo(pkg, 0);
+ final String appLabel = Objects.toString(ai.loadLabel(pm), "").trim();
+ if (appLabel.length() > 0) {
+ return appLabel;
+ }
+ } catch (NameNotFoundException e) { }
+ return pkg;
+ }
+
+ private void updateRemoteH(Token token, String name, PlaybackInfo pi) {
+ if (mCallbacks != null) {
+ mCallbacks.onRemoteUpdate(token, name, pi);
+ }
+ }
+
+ private static void dump(int n, PrintWriter writer, MediaController c) {
+ writer.println(" Controller " + n + ": " + c.getPackageName());
+ final Bundle extras = c.getExtras();
+ final long flags = c.getFlags();
+ final MediaMetadata mm = c.getMetadata();
+ final PlaybackInfo pi = c.getPlaybackInfo();
+ final PlaybackState playbackState = c.getPlaybackState();
+ final List<QueueItem> queue = c.getQueue();
+ final CharSequence queueTitle = c.getQueueTitle();
+ final int ratingType = c.getRatingType();
+ final PendingIntent sessionActivity = c.getSessionActivity();
+
+ writer.println(" PlaybackState: " + Util.playbackStateToString(playbackState));
+ writer.println(" PlaybackInfo: " + Util.playbackInfoToString(pi));
+ if (mm != null) {
+ writer.println(" MediaMetadata.desc=" + mm.getDescription());
+ }
+ writer.println(" RatingType: " + ratingType);
+ writer.println(" Flags: " + flags);
+ if (extras != null) {
+ writer.println(" Extras:");
+ for (String key : extras.keySet()) {
+ writer.println(" " + key + "=" + extras.get(key));
+ }
+ }
+ if (queueTitle != null) {
+ writer.println(" QueueTitle: " + queueTitle);
+ }
+ if (queue != null && !queue.isEmpty()) {
+ writer.println(" Queue:");
+ for (QueueItem qi : queue) {
+ writer.println(" " + qi);
+ }
+ }
+ if (pi != null) {
+ writer.println(" sessionActivity: " + sessionActivity);
+ }
+ }
+
+ public static void dumpMediaSessions(Context context) {
+ final MediaSessionManager mgr = (MediaSessionManager) context
+ .getSystemService(Context.MEDIA_SESSION_SERVICE);
+ try {
+ final List<MediaController> controllers = mgr.getActiveSessions(null);
+ final int N = controllers.size();
+ if (D.BUG) Log.d(TAG, N + " controllers");
+ for (int i = 0; i < N; i++) {
+ final StringWriter sw = new StringWriter();
+ final PrintWriter pw = new PrintWriter(sw, true);
+ dump(i + 1, pw, controllers.get(i));
+ if (D.BUG) Log.d(TAG, sw.toString());
+ }
+ } catch (SecurityException e) {
+ Log.w(TAG, "Not allowed to get sessions", e);
+ }
+ }
+
+ private final class MediaControllerRecord extends MediaController.Callback {
+ private final MediaController controller;
+
+ private boolean sentRemote;
+ private String name;
+
+ private MediaControllerRecord(MediaController controller) {
+ this.controller = controller;
+ }
+
+ private String cb(String method) {
+ return method + " " + controller.getPackageName() + " ";
+ }
+
+ @Override
+ public void onAudioInfoChanged(PlaybackInfo info) {
+ if (D.BUG) Log.d(TAG, cb("onAudioInfoChanged") + Util.playbackInfoToString(info)
+ + " sentRemote=" + sentRemote);
+ final boolean remote = isRemote(info);
+ if (!remote && sentRemote) {
+ mCallbacks.onRemoteRemoved(controller.getSessionToken());
+ sentRemote = false;
+ } else if (remote) {
+ updateRemoteH(controller.getSessionToken(), name, info);
+ sentRemote = true;
+ }
+ }
+
+ @Override
+ public void onExtrasChanged(Bundle extras) {
+ if (D.BUG) Log.d(TAG, cb("onExtrasChanged") + extras);
+ }
+
+ @Override
+ public void onMetadataChanged(MediaMetadata metadata) {
+ if (D.BUG) Log.d(TAG, cb("onMetadataChanged") + Util.mediaMetadataToString(metadata));
+ }
+
+ @Override
+ public void onPlaybackStateChanged(PlaybackState state) {
+ if (D.BUG) Log.d(TAG, cb("onPlaybackStateChanged") + Util.playbackStateToString(state));
+ }
+
+ @Override
+ public void onQueueChanged(List<QueueItem> queue) {
+ if (D.BUG) Log.d(TAG, cb("onQueueChanged") + queue);
+ }
+
+ @Override
+ public void onQueueTitleChanged(CharSequence title) {
+ if (D.BUG) Log.d(TAG, cb("onQueueTitleChanged") + title);
+ }
+
+ @Override
+ public void onSessionDestroyed() {
+ if (D.BUG) Log.d(TAG, cb("onSessionDestroyed"));
+ }
+
+ @Override
+ public void onSessionEvent(String event, Bundle extras) {
+ if (D.BUG) Log.d(TAG, cb("onSessionEvent") + "event=" + event + " extras=" + extras);
+ }
+ }
+
+ private final OnActiveSessionsChangedListener mSessionsListener =
+ new OnActiveSessionsChangedListener() {
+ @Override
+ public void onActiveSessionsChanged(List<MediaController> controllers) {
+ onActiveSessionsUpdatedH(controllers);
+ }
+ };
+
+ private final IRemoteVolumeController mRvc = new IRemoteVolumeController.Stub() {
+ @Override
+ public void remoteVolumeChanged(ISessionController session, int flags)
+ throws RemoteException {
+ mHandler.obtainMessage(H.REMOTE_VOLUME_CHANGED, flags, 0, session).sendToTarget();
+ }
+
+ @Override
+ public void updateRemoteController(final ISessionController session)
+ throws RemoteException {
+ mHandler.obtainMessage(H.UPDATE_REMOTE_CONTROLLER, session).sendToTarget();
+ }
+ };
+
+ private final class H extends Handler {
+ private static final int UPDATE_SESSIONS = 1;
+ private static final int REMOTE_VOLUME_CHANGED = 2;
+ private static final int UPDATE_REMOTE_CONTROLLER = 3;
+
+ private H(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case UPDATE_SESSIONS:
+ onActiveSessionsUpdatedH(mMgr.getActiveSessions(null));
+ break;
+ case REMOTE_VOLUME_CHANGED:
+ onRemoteVolumeChangedH((ISessionController) msg.obj, msg.arg1);
+ break;
+ case UPDATE_REMOTE_CONTROLLER:
+ onUpdateRemoteControllerH((ISessionController) msg.obj);
+ break;
+ }
+ }
+ }
+
+ public interface Callbacks {
+ void onRemoteUpdate(Token token, String name, PlaybackInfo pi);
+ void onRemoteRemoved(Token t);
+ void onRemoteVolumeChanged(Token token, int flags);
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/Prefs.java b/packages/SystemUI/src/com/android/systemui/volume/Prefs.java
new file mode 100644
index 0000000..58bc9f4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/Prefs.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.preference.PreferenceManager;
+
+/**
+ * Configuration for the volume dialog + related policy.
+ */
+public class Prefs {
+
+ public static final String PREF_ENABLE_PROTOTYPE = "pref_enable_prototype"; // not persistent
+ public static final String PREF_SHOW_ALARMS = "pref_show_alarms";
+ public static final String PREF_SHOW_SYSTEM = "pref_show_system";
+ public static final String PREF_SHOW_HEADERS = "pref_show_headers";
+ public static final String PREF_SHOW_FAKE_REMOTE_1 = "pref_show_fake_remote_1";
+ public static final String PREF_SHOW_FAKE_REMOTE_2 = "pref_show_fake_remote_2";
+ public static final String PREF_SHOW_FOOTER = "pref_show_footer";
+ public static final String PREF_ZEN_FOOTER = "pref_zen_footer";
+ public static final String PREF_ENABLE_AUTOMUTE = "pref_enable_automute";
+ public static final String PREF_ENABLE_SILENT_MODE = "pref_enable_silent_mode";
+ public static final String PREF_DEBUG_LOGGING = "pref_debug_logging";
+ public static final String PREF_SEND_LOGS = "pref_send_logs";
+ public static final String PREF_ADJUST_SYSTEM = "pref_adjust_system";
+ public static final String PREF_ADJUST_VOICE_CALLS = "pref_adjust_voice_calls";
+ public static final String PREF_ADJUST_BLUETOOTH_SCO = "pref_adjust_bluetooth_sco";
+ public static final String PREF_ADJUST_MEDIA = "pref_adjust_media";
+ public static final String PREF_ADJUST_ALARMS = "pref_adjust_alarms";
+ public static final String PREF_ADJUST_NOTIFICATION = "pref_adjust_notification";
+
+ public static final boolean DEFAULT_SHOW_HEADERS = true;
+ public static final boolean DEFAULT_SHOW_FOOTER = true;
+ public static final boolean DEFAULT_ENABLE_AUTOMUTE = true;
+ public static final boolean DEFAULT_ENABLE_SILENT_MODE = true;
+ public static final boolean DEFAULT_ZEN_FOOTER = true;
+
+ public static void unregisterCallbacks(Context c, OnSharedPreferenceChangeListener listener) {
+ prefs(c).unregisterOnSharedPreferenceChangeListener(listener);
+ }
+
+ public static void registerCallbacks(Context c, OnSharedPreferenceChangeListener listener) {
+ prefs(c).registerOnSharedPreferenceChangeListener(listener);
+ }
+
+ private static SharedPreferences prefs(Context context) {
+ return PreferenceManager.getDefaultSharedPreferences(context);
+ }
+
+ public static boolean get(Context context, String key, boolean def) {
+ return prefs(context).getBoolean(key, def);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java b/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
index 5f5b881..4f20ac7 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
@@ -60,17 +60,14 @@ public class SegmentedButtons extends LinearLayout {
final Object tag = c.getTag();
final boolean selected = Objects.equals(mSelectedValue, tag);
c.setSelected(selected);
- c.getCompoundDrawables()[1].setTint(mContext.getColor(selected
- ? R.color.segmented_button_selected : R.color.segmented_button_unselected));
}
fireOnSelected();
}
- public void addButton(int labelResId, int iconResId, Object value) {
+ public void addButton(int labelResId, Object value) {
final Button b = (Button) mInflater.inflate(R.layout.segmented_button, this, false);
b.setTag(LABEL_RES_KEY, labelResId);
b.setText(labelResId);
- b.setCompoundDrawablesWithIntrinsicBounds(0, iconResId, 0, 0);
final LayoutParams lp = (LayoutParams) b.getLayoutParams();
if (getChildCount() == 0) {
lp.leftMargin = lp.rightMargin = 0; // first button has no margin
diff --git a/packages/SystemUI/src/com/android/systemui/volume/SpTexts.java b/packages/SystemUI/src/com/android/systemui/volume/SpTexts.java
new file mode 100644
index 0000000..d8e53db
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/SpTexts.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.ArrayMap;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.View.OnAttachStateChangeListener;
+import android.widget.TextView;
+
+/**
+ * Capture initial sp values for registered textviews, and update properly when configuration
+ * changes.
+ */
+public class SpTexts {
+
+ private final Context mContext;
+ private final ArrayMap<TextView, Integer> mTexts = new ArrayMap<>();
+
+ public SpTexts(Context context) {
+ mContext = context;
+ }
+
+ public int add(final TextView text) {
+ if (text == null) return 0;
+ final Resources res = mContext.getResources();
+ final float fontScale = res.getConfiguration().fontScale;
+ final float density = res.getDisplayMetrics().density;
+ final float px = text.getTextSize();
+ final int sp = (int)(px / fontScale / density);
+ mTexts.put(text, sp);
+ text.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ }
+
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ setTextSizeH(text, sp);
+ }
+ });
+ return sp;
+ }
+
+ public void update() {
+ if (mTexts.isEmpty()) return;
+ mTexts.keyAt(0).post(mUpdateAll);
+ }
+
+ private void setTextSizeH(TextView text, int sp) {
+ text.setTextSize(TypedValue.COMPLEX_UNIT_SP, sp);
+ }
+
+ private final Runnable mUpdateAll = new Runnable() {
+ @Override
+ public void run() {
+ for (int i = 0; i < mTexts.size(); i++) {
+ setTextSizeH(mTexts.keyAt(i), mTexts.valueAt(i));
+ }
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/Util.java b/packages/SystemUI/src/com/android/systemui/volume/Util.java
new file mode 100644
index 0000000..fbdc1ca
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/Util.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume;
+
+import android.media.AudioManager;
+import android.media.MediaMetadata;
+import android.media.VolumeProvider;
+import android.media.session.MediaController.PlaybackInfo;
+import android.media.session.PlaybackState;
+import android.service.notification.ZenModeConfig.DowntimeInfo;
+import android.view.View;
+import android.widget.TextView;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * Static helpers for the volume dialog.
+ */
+class Util {
+
+ // Note: currently not shown (only used in the text footer)
+ private static final SimpleDateFormat HMMAA = new SimpleDateFormat("h:mm aa", Locale.US);
+
+ private static int[] AUDIO_MANAGER_FLAGS = new int[] {
+ AudioManager.FLAG_SHOW_UI,
+ AudioManager.FLAG_VIBRATE,
+ AudioManager.FLAG_PLAY_SOUND,
+ AudioManager.FLAG_ALLOW_RINGER_MODES,
+ AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE,
+ AudioManager.FLAG_SHOW_VIBRATE_HINT,
+ AudioManager.FLAG_SHOW_SILENT_HINT,
+ AudioManager.FLAG_FROM_KEY,
+ };
+
+ private static String[] AUDIO_MANAGER_FLAG_NAMES = new String[] {
+ "SHOW_UI",
+ "VIBRATE",
+ "PLAY_SOUND",
+ "ALLOW_RINGER_MODES",
+ "REMOVE_SOUND_AND_VIBRATE",
+ "SHOW_VIBRATE_HINT",
+ "SHOW_SILENT_HINT",
+ "FROM_KEY",
+ };
+
+ public static String logTag(Class<?> c) {
+ final String tag = "vol." + c.getSimpleName();
+ return tag.length() < 23 ? tag : tag.substring(0, 23);
+ }
+
+ public static String ringerModeToString(int ringerMode) {
+ switch (ringerMode) {
+ case AudioManager.RINGER_MODE_SILENT: return "RINGER_MODE_SILENT";
+ case AudioManager.RINGER_MODE_VIBRATE: return "RINGER_MODE_VIBRATE";
+ case AudioManager.RINGER_MODE_NORMAL: return "RINGER_MODE_NORMAL";
+ default: return "RINGER_MODE_UNKNOWN_" + ringerMode;
+ }
+ }
+
+ public static String mediaMetadataToString(MediaMetadata metadata) {
+ return metadata.getDescription().toString();
+ }
+
+ public static String playbackInfoToString(PlaybackInfo info) {
+ if (info == null) return null;
+ final String type = playbackInfoTypeToString(info.getPlaybackType());
+ final String vc = volumeProviderControlToString(info.getVolumeControl());
+ return String.format("PlaybackInfo[vol=%s,max=%s,type=%s,vc=%s],atts=%s",
+ info.getCurrentVolume(), info.getMaxVolume(), type, vc, info.getAudioAttributes());
+ }
+
+ public static String playbackInfoTypeToString(int type) {
+ switch (type) {
+ case PlaybackInfo.PLAYBACK_TYPE_LOCAL: return "LOCAL";
+ case PlaybackInfo.PLAYBACK_TYPE_REMOTE: return "REMOTE";
+ default: return "UNKNOWN_" + type;
+ }
+ }
+
+ public static String playbackStateStateToString(int state) {
+ switch (state) {
+ case PlaybackState.STATE_NONE: return "STATE_NONE";
+ case PlaybackState.STATE_STOPPED: return "STATE_STOPPED";
+ case PlaybackState.STATE_PAUSED: return "STATE_PAUSED";
+ case PlaybackState.STATE_PLAYING: return "STATE_PLAYING";
+ default: return "UNKNOWN_" + state;
+ }
+ }
+
+ public static String volumeProviderControlToString(int control) {
+ switch (control) {
+ case VolumeProvider.VOLUME_CONTROL_ABSOLUTE: return "VOLUME_CONTROL_ABSOLUTE";
+ case VolumeProvider.VOLUME_CONTROL_FIXED: return "VOLUME_CONTROL_FIXED";
+ case VolumeProvider.VOLUME_CONTROL_RELATIVE: return "VOLUME_CONTROL_RELATIVE";
+ default: return "VOLUME_CONTROL_UNKNOWN_" + control;
+ }
+ }
+
+ public static String playbackStateToString(PlaybackState playbackState) {
+ if (playbackState == null) return null;
+ return playbackStateStateToString(playbackState.getState()) + " " + playbackState;
+ }
+
+ public static String audioManagerFlagsToString(int value) {
+ return bitFieldToString(value, AUDIO_MANAGER_FLAGS, AUDIO_MANAGER_FLAG_NAMES);
+ }
+
+ private static String bitFieldToString(int value, int[] values, String[] names) {
+ if (value == 0) return "";
+ final StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < values.length; i++) {
+ if ((value & values[i]) != 0) {
+ if (sb.length() > 0) sb.append(',');
+ sb.append(names[i]);
+ }
+ value &= ~values[i];
+ }
+ if (value != 0) {
+ if (sb.length() > 0) sb.append(',');
+ sb.append("UNKNOWN_").append(value);
+ }
+ return sb.toString();
+ }
+
+ public static String getShortTime(long millis) {
+ return HMMAA.format(new Date(millis));
+ }
+
+ public static String getShortTime(DowntimeInfo info) {
+ return ((info.endHour + 1) % 12) + ":" + (info.endMinute < 10 ? " " : "") + info.endMinute;
+ }
+
+ public static void setText(TextView tv, CharSequence text) {
+ if (Objects.equals(tv.getText(), text)) return;
+ tv.setText(text);
+ }
+
+ public static final void setVisOrGone(View v, boolean vis) {
+ if (v == null || (v.getVisibility() == View.VISIBLE) == vis) return;
+ v.setVisibility(vis ? View.VISIBLE : View.GONE);
+ }
+
+ public static final void setVisOrInvis(View v, boolean vis) {
+ if (v == null || (v.getVisibility() == View.VISIBLE) == vis) return;
+ v.setVisibility(vis ? View.VISIBLE : View.INVISIBLE);
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeComponent.java
index e3f8f3d..1f0ee57 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeComponent.java
@@ -16,10 +16,18 @@
package com.android.systemui.volume;
+import android.content.res.Configuration;
+
import com.android.systemui.DemoMode;
import com.android.systemui.statusbar.policy.ZenModeController;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
public interface VolumeComponent extends DemoMode {
ZenModeController getZenController();
void dismissNow();
+ void onConfigurationChanged(Configuration newConfig);
+ void dump(FileDescriptor fd, PrintWriter pw, String[] args);
+ void register();
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
new file mode 100644
index 0000000..5fbb18d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
@@ -0,0 +1,1039 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume;
+
+import android.animation.LayoutTransition;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.annotation.SuppressLint;
+import android.app.Dialog;
+import android.app.KeyguardManager;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.AnimatedVectorDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
+import android.media.AudioSystem;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.provider.Settings.Global;
+import android.service.notification.ZenModeConfig;
+import android.service.notification.ZenModeConfig.DowntimeInfo;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.SparseBooleanArray;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnLayoutChangeListener;
+import android.view.View.OnTouchListener;
+import android.view.ViewGroup;
+import android.view.ViewGroup.MarginLayoutParams;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.animation.DecelerateInterpolator;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.volume.VolumeDialogController.State;
+import com.android.systemui.volume.VolumeDialogController.StreamState;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Visual presentation of the volume dialog.
+ *
+ * A client of VolumeDialogController and its state model.
+ *
+ * Methods ending in "H" must be called on the (ui) handler.
+ */
+public class VolumeDialog {
+ private static final String TAG = Util.logTag(VolumeDialog.class);
+
+ private static final long USER_ATTEMPT_GRACE_PERIOD = 1000;
+ private static final int WAIT_FOR_RIPPLE = 200;
+ private static final int UPDATE_ANIMATION_DURATION = 80;
+
+ private final Context mContext;
+ private final H mHandler = new H();
+ private final VolumeDialogController mController;
+
+ private final CustomDialog mDialog;
+ private final ViewGroup mDialogView;
+ private final ViewGroup mDialogContentView;
+ private final ImageButton mExpandButton;
+ private final TextView mFootlineText;
+ private final Button mFootlineAction;
+ private final View mSettingsButton;
+ private final View mFooter;
+ private final List<VolumeRow> mRows = new ArrayList<VolumeRow>();
+ private final SpTexts mSpTexts;
+ private final SparseBooleanArray mDynamic = new SparseBooleanArray();
+ private final KeyguardManager mKeyguard;
+ private final int mExpandButtonAnimationDuration;
+ private final View mTextFooter;
+ private final ZenFooter mZenFooter;
+ private final LayoutTransition mLayoutTransition;
+
+ private boolean mShowing;
+ private boolean mExpanded;
+ private int mActiveStream;
+ private boolean mShowHeaders = Prefs.DEFAULT_SHOW_HEADERS;
+ private boolean mShowFooter = Prefs.DEFAULT_SHOW_FOOTER;
+ private boolean mShowZenFooter = Prefs.DEFAULT_ZEN_FOOTER;
+ private boolean mAutomute = Prefs.DEFAULT_ENABLE_AUTOMUTE;
+ private boolean mSilentMode = Prefs.DEFAULT_ENABLE_SILENT_MODE;
+ private State mState;
+ private int mExpandAnimRes;
+ private boolean mExpanding;
+
+ public VolumeDialog(Context context, VolumeDialogController controller,
+ ZenModeController zenModeController) {
+ mContext = context;
+ mController = controller;
+ mSpTexts = new SpTexts(mContext);
+ mKeyguard = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
+
+ mDialog = new CustomDialog(mContext);
+
+ final Window window = mDialog.getWindow();
+ window.requestFeature(Window.FEATURE_NO_TITLE);
+ window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+ window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+ window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+ | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
+ mDialog.setCanceledOnTouchOutside(true);
+ final Resources res = mContext.getResources();
+ final WindowManager.LayoutParams lp = window.getAttributes();
+ lp.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
+ lp.format = PixelFormat.TRANSLUCENT;
+ lp.setTitle(VolumeDialog.class.getSimpleName());
+ lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
+ lp.windowAnimations = R.style.VolumeDialogAnimations;
+ lp.y = res.getDimensionPixelSize(R.dimen.volume_offset_top);
+ lp.gravity = Gravity.TOP;
+ window.setAttributes(lp);
+
+ mDialog.setContentView(R.layout.volume_dialog);
+ mDialogView = (ViewGroup) mDialog.findViewById(R.id.volume_dialog);
+ mDialogContentView = (ViewGroup) mDialog.findViewById(R.id.volume_dialog_content);
+ mExpandButton = (ImageButton) mDialogView.findViewById(R.id.volume_expand_button);
+ mExpandButton.setOnClickListener(mClickExpand);
+ updateWindowWidthH();
+ updateExpandButtonH();
+ mLayoutTransition = new LayoutTransition();
+ mLayoutTransition.setDuration(new ValueAnimator().getDuration() / 2);
+ mLayoutTransition.disableTransitionType(LayoutTransition.DISAPPEARING);
+ mLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
+ mDialogContentView.setLayoutTransition(mLayoutTransition);
+
+ addRow(AudioManager.STREAM_RING,
+ R.drawable.ic_volume_ringer, R.drawable.ic_volume_ringer_mute, true);
+ addRow(AudioManager.STREAM_MUSIC,
+ R.drawable.ic_volume_media, R.drawable.ic_volume_media_mute, true);
+ addRow(AudioManager.STREAM_ALARM,
+ R.drawable.ic_volume_alarm, R.drawable.ic_volume_alarm_mute, false);
+ addRow(AudioManager.STREAM_VOICE_CALL,
+ R.drawable.ic_volume_voice, R.drawable.ic_volume_voice, false);
+ addRow(AudioManager.STREAM_BLUETOOTH_SCO,
+ R.drawable.ic_volume_bt_sco, R.drawable.ic_volume_bt_sco, false);
+ addRow(AudioManager.STREAM_SYSTEM,
+ R.drawable.ic_volume_system, R.drawable.ic_volume_system_mute, false);
+
+ mTextFooter = mDialog.findViewById(R.id.volume_text_footer);
+ mFootlineText = (TextView) mDialog.findViewById(R.id.volume_footline_text);
+ mSpTexts.add(mFootlineText);
+ mFootlineAction = (Button) mDialog.findViewById(R.id.volume_footline_action_button);
+ mSpTexts.add(mFootlineAction);
+ mFooter = mDialog.findViewById(R.id.volume_footer);
+ mSettingsButton = mDialog.findViewById(R.id.volume_settings_button);
+ mSettingsButton.setOnClickListener(mClickSettings);
+ mExpandButtonAnimationDuration = res.getInteger(R.integer.volume_expand_animation_duration);
+ mZenFooter = (ZenFooter) mDialog.findViewById(R.id.volume_zen_footer);
+ mZenFooter.init(zenModeController, mZenFooterCallback);
+
+ controller.addCallback(mControllerCallbackH, mHandler);
+ controller.getState();
+ }
+
+ private void updateWindowWidthH() {
+ final ViewGroup.LayoutParams lp = mDialogView.getLayoutParams();
+ final DisplayMetrics dm = mContext.getResources().getDisplayMetrics();
+ if (D.BUG) Log.d(TAG, "updateWindowWidth dm.w=" + dm.widthPixels);
+ int w = dm.widthPixels;
+ final int max = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.standard_notification_panel_width);
+ if (w > max) {
+ w = max;
+ }
+ w -= mContext.getResources().getDimensionPixelSize(R.dimen.notification_side_padding) * 2;
+ lp.width = w;
+ mDialogView.setLayoutParams(lp);
+ }
+
+ public void setStreamImportant(int stream, boolean important) {
+ mHandler.obtainMessage(H.SET_STREAM_IMPORTANT, stream, important ? 1 : 0).sendToTarget();
+ }
+
+ public void setShowHeaders(boolean showHeaders) {
+ if (showHeaders == mShowHeaders) return;
+ mShowHeaders = showHeaders;
+ mHandler.sendEmptyMessage(H.RECHECK_ALL);
+ }
+
+ public void setShowFooter(boolean show) {
+ if (mShowFooter == show) return;
+ mShowFooter = show;
+ mHandler.sendEmptyMessage(H.RECHECK_ALL);
+ }
+
+ public void setZenFooter(boolean zen) {
+ if (mShowZenFooter == zen) return;
+ mShowZenFooter = zen;
+ mHandler.sendEmptyMessage(H.RECHECK_ALL);
+ }
+
+ public void setAutomute(boolean automute) {
+ if (mAutomute == automute) return;
+ mAutomute = automute;
+ mHandler.sendEmptyMessage(H.RECHECK_ALL);
+ }
+
+ public void setSilentMode(boolean silentMode) {
+ if (mSilentMode == silentMode) return;
+ mSilentMode = silentMode;
+ mHandler.sendEmptyMessage(H.RECHECK_ALL);
+ }
+
+ private void addRow(int stream, int iconRes, int iconMuteRes, boolean important) {
+ final VolumeRow row = initRow(stream, iconRes, iconMuteRes, important);
+ if (!mRows.isEmpty()) {
+ final View v = new View(mContext);
+ final int h = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.volume_slider_interspacing);
+ final LinearLayout.LayoutParams lp =
+ new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, h);
+ mDialogContentView.addView(v, mDialogContentView.getChildCount() - 1, lp);
+ row.space = v;
+ }
+ row.settingsButton.addOnLayoutChangeListener(new OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ if (D.BUG) Log.d(TAG, "onLayoutChange"
+ + " old=" + new Rect(oldLeft, oldTop, oldRight, oldBottom).toShortString()
+ + " new=" + new Rect(left,top,right,bottom).toShortString());
+ if (oldLeft != left || oldTop != top) {
+ for (int i = 0; i < mDialogContentView.getChildCount(); i++) {
+ final View c = mDialogContentView.getChildAt(i);
+ if (!c.isShown()) continue;
+ if (c == row.view) {
+ repositionExpandAnim(row);
+ }
+ return;
+ }
+ }
+ }
+ });
+ // add new row just before the footer
+ mDialogContentView.addView(row.view, mDialogContentView.getChildCount() - 1);
+ mRows.add(row);
+ }
+
+ private boolean isAttached() {
+ return mDialogContentView != null && mDialogContentView.isAttachedToWindow();
+ }
+
+ private VolumeRow getActiveRow() {
+ for (VolumeRow row : mRows) {
+ if (row.stream == mActiveStream) {
+ return row;
+ }
+ }
+ return mRows.get(0);
+ }
+
+ private VolumeRow findRow(int stream) {
+ for (VolumeRow row : mRows) {
+ if (row.stream == stream) return row;
+ }
+ return null;
+ }
+
+ private void repositionExpandAnim(VolumeRow row) {
+ final int[] loc = new int[2];
+ row.settingsButton.getLocationInWindow(loc);
+ final MarginLayoutParams mlp = (MarginLayoutParams) mDialogView.getLayoutParams();
+ final int x = loc[0] - mlp.leftMargin;
+ final int y = loc[1] - mlp.topMargin;
+ if (D.BUG) Log.d(TAG, "repositionExpandAnim x=" + x + " y=" + y);
+ mExpandButton.setTranslationX(x);
+ mExpandButton.setTranslationY(y);
+ }
+
+ public void dump(PrintWriter writer) {
+ writer.println(VolumeDialog.class.getSimpleName() + " state:");
+ writer.print(" mShowing: "); writer.println(mShowing);
+ writer.print(" mExpanded: "); writer.println(mExpanded);
+ writer.print(" mExpanding: "); writer.println(mExpanding);
+ writer.print(" mActiveStream: "); writer.println(mActiveStream);
+ writer.print(" mDynamic: "); writer.println(mDynamic);
+ writer.print(" mShowHeaders: "); writer.println(mShowHeaders);
+ writer.print(" mShowFooter: "); writer.println(mShowFooter);
+ writer.print(" mAutomute: "); writer.println(mAutomute);
+ writer.print(" mSilentMode: "); writer.println(mSilentMode);
+ }
+
+ private static int getImpliedLevel(SeekBar seekBar, int progress) {
+ final int m = seekBar.getMax();
+ final int n = m / 100 - 1;
+ final int level = progress == 0 ? 0
+ : progress == m ? (m / 100) : (1 + (int)((progress / (float) m) * n));
+ return level;
+ }
+
+ @SuppressLint("InflateParams")
+ private VolumeRow initRow(final int stream, int iconRes, int iconMuteRes, boolean important) {
+ final VolumeRow row = new VolumeRow();
+ row.stream = stream;
+ row.iconRes = iconRes;
+ row.iconMuteRes = iconMuteRes;
+ row.important = important;
+ row.view = mDialog.getLayoutInflater().inflate(R.layout.volume_dialog_row, null);
+ row.view.setTag(row);
+ row.header = (TextView) row.view.findViewById(R.id.volume_row_header);
+ mSpTexts.add(row.header);
+ row.slider = (SeekBar) row.view.findViewById(R.id.volume_row_slider);
+ row.slider.setOnSeekBarChangeListener(new VolumeSeekBarChangeListener(row));
+
+ // forward events above the slider into the slider
+ row.view.setOnTouchListener(new OnTouchListener() {
+ private final Rect mSliderHitRect = new Rect();
+ private boolean mDragging;
+
+ @SuppressLint("ClickableViewAccessibility")
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ row.slider.getHitRect(mSliderHitRect);
+ if (!mDragging && event.getActionMasked() == MotionEvent.ACTION_DOWN
+ && event.getY() < mSliderHitRect.top) {
+ mDragging = true;
+ }
+ if (mDragging) {
+ event.offsetLocation(-mSliderHitRect.left, -mSliderHitRect.top);
+ row.slider.dispatchTouchEvent(event);
+ if (event.getActionMasked() == MotionEvent.ACTION_UP
+ || event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
+ mDragging = false;
+ }
+ return true;
+ }
+ return false;
+ }
+ });
+ row.icon = (ImageButton) row.view.findViewById(R.id.volume_row_icon);
+ row.icon.setImageResource(iconRes);
+ row.icon.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Events.writeEvent(Events.EVENT_ICON_CLICK, row.stream, row.iconState);
+ mController.setActiveStream(row.stream);
+ if (row.stream == AudioManager.STREAM_RING) {
+ final boolean hasVibrator = mController.hasVibrator();
+ if (mState.ringerModeInternal == AudioManager.RINGER_MODE_NORMAL) {
+ if (hasVibrator) {
+ mController.setRingerMode(AudioManager.RINGER_MODE_VIBRATE, false);
+ } else {
+ final boolean wasZero = row.ss.level == 0;
+ mController.setStreamVolume(stream, wasZero ? row.lastAudibleLevel : 0);
+ }
+ } else {
+ mController.setRingerMode(AudioManager.RINGER_MODE_NORMAL, false);
+ if (row.ss.level == 0) {
+ mController.setStreamVolume(stream, 1);
+ }
+ }
+ } else {
+ if (mAutomute && !row.ss.muteSupported) {
+ final boolean vmute = row.ss.level == 0;
+ mController.setStreamVolume(stream, vmute ? row.lastAudibleLevel : 0);
+ } else {
+ final boolean mute = !row.ss.muted;
+ mController.setStreamMute(stream, mute);
+ if (mAutomute) {
+ if (!mute && row.ss.level == 0) {
+ mController.setStreamVolume(stream, 1);
+ }
+ }
+ }
+ }
+ row.userAttempt = 0; // reset the grace period, slider should update immediately
+ }
+ });
+ row.settingsButton = (ImageButton) row.view.findViewById(R.id.volume_settings_button);
+ row.settingsButton.setOnClickListener(mClickSettings);
+ return row;
+ }
+
+ public void destroy() {
+ mController.removeCallback(mControllerCallbackH);
+ }
+
+ public void show(int reason) {
+ mHandler.obtainMessage(H.SHOW, reason, 0).sendToTarget();
+ }
+
+ public void dismiss(int reason) {
+ mHandler.obtainMessage(H.DISMISS, reason, 0).sendToTarget();
+ }
+
+ protected void onSettingsClickedH() {
+ // hook for subclasses
+ }
+
+ protected void onZenSettingsClickedH() {
+ // hook for subclasses
+ }
+
+ private void showH(int reason) {
+ mHandler.removeMessages(H.SHOW);
+ mHandler.removeMessages(H.DISMISS);
+ rescheduleTimeoutH();
+ if (mShowing) return;
+ mShowing = true;
+ mDialog.show();
+ Events.writeEvent(Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
+ mController.notifyVisible(true);
+ }
+
+ protected void rescheduleTimeoutH() {
+ mHandler.removeMessages(H.DISMISS);
+ final int timeout = computeTimeoutH();
+ if (D.BUG) Log.d(TAG, "rescheduleTimeout " + timeout);
+ mHandler.sendMessageDelayed(mHandler
+ .obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT, 0), timeout);
+ mController.userActivity();
+ }
+
+ private int computeTimeoutH() {
+ if (mZenFooter != null && mZenFooter.isFooterExpanded()) return 10000;
+ if (mExpanded || mExpanding) return 5000;
+ if (mActiveStream == AudioManager.STREAM_MUSIC) return 1500;
+ return 3000;
+ }
+
+ protected void dismissH(int reason) {
+ mHandler.removeMessages(H.DISMISS);
+ mHandler.removeMessages(H.SHOW);
+ if (!mShowing) return;
+ mShowing = false;
+ mDialog.dismiss();
+ Events.writeEvent(Events.EVENT_DISMISS_DIALOG, reason);
+ setExpandedH(false);
+ mController.notifyVisible(false);
+ }
+
+ private void setExpandedH(boolean expanded) {
+ if (mExpanded == expanded) return;
+ mExpanded = expanded;
+ mExpanding = isAttached();
+ if (D.BUG) Log.d(TAG, "setExpandedH " + expanded);
+ updateRowsH();
+ if (mExpanding) {
+ final Drawable d = mExpandButton.getDrawable();
+ if (d instanceof AnimatedVectorDrawable) {
+ // workaround to reset drawable
+ final AnimatedVectorDrawable avd = (AnimatedVectorDrawable) d.getConstantState()
+ .newDrawable();
+ mExpandButton.setImageDrawable(avd);
+ avd.start();
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ mExpanding = false;
+ updateExpandButtonH();
+ rescheduleTimeoutH();
+ }
+ }, mExpandButtonAnimationDuration);
+ }
+ }
+ rescheduleTimeoutH();
+ }
+
+ private void updateExpandButtonH() {
+ mExpandButton.setClickable(!mExpanding);
+ if (mExpanding && isAttached()) return;
+ final int res = mExpanded ? R.drawable.ic_volume_collapse_animation
+ : R.drawable.ic_volume_expand_animation;
+ if (res == mExpandAnimRes) return;
+ mExpandAnimRes = res;
+ mExpandButton.setImageResource(res);
+ }
+
+ private boolean isVisibleH(VolumeRow row, boolean isActive) {
+ return mExpanded && row.view.getVisibility() == View.VISIBLE
+ || (mExpanded && (row.important || isActive))
+ || !mExpanded && isActive;
+ }
+
+ private void updateRowsH() {
+ final VolumeRow activeRow = getActiveRow();
+ updateFooterH();
+ updateExpandButtonH();
+ final boolean footerVisible = mFooter.getVisibility() == View.VISIBLE;
+ if (!mShowing) {
+ trimObsoleteH();
+ }
+ // first, find the last visible row
+ VolumeRow lastVisible = null;
+ for (VolumeRow row : mRows) {
+ final boolean isActive = row == activeRow;
+ if (isVisibleH(row, isActive)) {
+ lastVisible = row;
+ }
+ }
+ // apply changes to all rows
+ for (VolumeRow row : mRows) {
+ final boolean isActive = row == activeRow;
+ final boolean visible = isVisibleH(row, isActive);
+ Util.setVisOrGone(row.view, visible);
+ Util.setVisOrGone(row.space, visible && mExpanded);
+ final int expandButtonRes = mExpanded ? R.drawable.ic_volume_settings : 0;
+ if (expandButtonRes != row.cachedExpandButtonRes) {
+ row.cachedExpandButtonRes = expandButtonRes;
+ if (expandButtonRes == 0) {
+ row.settingsButton.setImageDrawable(null);
+ } else {
+ row.settingsButton.setImageResource(expandButtonRes);
+ }
+ }
+ Util.setVisOrInvis(row.settingsButton,
+ mExpanded && (!footerVisible && row == lastVisible));
+ row.header.setAlpha(mExpanded && isActive ? 1 : 0.5f);
+ }
+ }
+
+ private void trimObsoleteH() {
+ for (int i = mRows.size() -1; i >= 0; i--) {
+ final VolumeRow row = mRows.get(i);
+ if (row.ss == null || !row.ss.dynamic) continue;
+ if (!mDynamic.get(row.stream)) {
+ mRows.remove(i);
+ mDialogContentView.removeView(row.view);
+ mDialogContentView.removeView(row.space);
+ }
+ }
+ }
+
+ private void onStateChangedH(State state) {
+ mState = state;
+ mDynamic.clear();
+ // add any new dynamic rows
+ for (int i = 0; i < state.states.size(); i++) {
+ final int stream = state.states.keyAt(i);
+ final StreamState ss = state.states.valueAt(i);
+ if (!ss.dynamic) continue;
+ mDynamic.put(stream, true);
+ if (findRow(stream) == null) {
+ addRow(stream, R.drawable.ic_volume_remote, R.drawable.ic_volume_remote_mute, true);
+ }
+ }
+
+ if (mActiveStream != state.activeStream) {
+ mActiveStream = state.activeStream;
+ updateRowsH();
+ rescheduleTimeoutH();
+ }
+ for (VolumeRow row : mRows) {
+ updateVolumeRowH(row);
+ }
+ updateFooterH();
+ }
+
+ private void updateTextFooterH() {
+ final boolean zen = mState.zenMode != Global.ZEN_MODE_OFF;
+ final boolean wasVisible = mFooter.getVisibility() == View.VISIBLE;
+ Util.setVisOrGone(mTextFooter, mExpanded && mShowFooter && (zen || mShowing && wasVisible));
+ if (mTextFooter.getVisibility() == View.VISIBLE) {
+ String text = null;
+ String action = null;
+ if (mState.exitCondition != null) {
+ final long countdown = ZenModeConfig.tryParseCountdownConditionId(mState
+ .exitCondition.id);
+ if (countdown != 0) {
+ text = mContext.getString(R.string.volume_dnd_ends_at,
+ Util.getShortTime(countdown));
+ action = mContext.getString(R.string.volume_end_now);
+ } else {
+ final DowntimeInfo info = ZenModeConfig.tryParseDowntimeConditionId(mState.
+ exitCondition.id);
+ if (info != null) {
+ text = mContext.getString(R.string.volume_dnd_ends_at,
+ Util.getShortTime(info));
+ action = mContext.getString(R.string.volume_end_now);
+ }
+ }
+ }
+ if (text == null) {
+ text = mContext.getString(R.string.volume_dnd_is_on);
+ }
+ if (action == null) {
+ action = mContext.getString(R.string.volume_turn_off);
+ }
+ Util.setText(mFootlineText, text);
+ Util.setText(mFootlineAction, action);
+ mFootlineAction.setOnClickListener(mTurnOffDnd);
+ }
+ Util.setVisOrGone(mFootlineText, zen);
+ Util.setVisOrGone(mFootlineAction, zen);
+ }
+
+ private void updateFooterH() {
+ if (!mShowFooter) {
+ Util.setVisOrGone(mFooter, false);
+ return;
+ }
+ if (mShowZenFooter) {
+ Util.setVisOrGone(mTextFooter, false);
+ final boolean ringActive = mActiveStream == AudioManager.STREAM_RING;
+ Util.setVisOrGone(mZenFooter, mZenFooter.isZen() && ringActive
+ || mShowing && (mExpanded || mZenFooter.getVisibility() == View.VISIBLE));
+ mZenFooter.update();
+ } else {
+ Util.setVisOrGone(mZenFooter, false);
+ updateTextFooterH();
+ }
+ }
+
+ private void updateVolumeRowH(VolumeRow row) {
+ if (mState == null) return;
+ final StreamState ss = mState.states.get(row.stream);
+ if (ss == null) return;
+ row.ss = ss;
+ if (ss.level > 0) {
+ row.lastAudibleLevel = ss.level;
+ }
+ final boolean isRingStream = row.stream == AudioManager.STREAM_RING;
+ final boolean isSystemStream = row.stream == AudioManager.STREAM_SYSTEM;
+ final boolean isRingVibrate = isRingStream
+ && mState.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE;
+ final boolean isNoned = (isRingStream || isSystemStream)
+ && mState.zenMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
+ final boolean isLimited = isRingStream
+ && mState.zenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ final boolean isRingAndSuppressed = isRingStream && mState.effectsSuppressor != null;
+
+ // update slider max
+ final int max = ss.levelMax * 100;
+ if (max != row.slider.getMax()) {
+ row.slider.setMax(max);
+ }
+
+ // update header visible
+ if (row.cachedShowHeaders != mShowHeaders) {
+ row.cachedShowHeaders = mShowHeaders;
+ Util.setVisOrGone(row.header, mShowHeaders);
+ }
+
+ // update header text
+ final String text;
+ if (isRingAndSuppressed) {
+ text = mContext.getString(R.string.volume_stream_suppressed, ss.name,
+ mState.effectsSuppressorName);
+ } else if (isNoned) {
+ text = mContext.getString(R.string.volume_stream_muted_dnd, ss.name);
+ } else if (isRingVibrate && isLimited) {
+ text = mContext.getString(R.string.volume_stream_vibrate_dnd, ss.name);
+ } else if (isRingVibrate) {
+ text = mContext.getString(R.string.volume_stream_vibrate, ss.name);
+ } else if (ss.muted || mAutomute && ss.level == 0) {
+ text = mContext.getString(R.string.volume_stream_muted, ss.name);
+ } else if (isLimited) {
+ text = mContext.getString(R.string.volume_stream_limited_dnd, ss.name);
+ } else {
+ text = ss.name;
+ }
+ Util.setText(row.header, text);
+
+ // update icon
+ final boolean iconEnabled = !isRingAndSuppressed && (mAutomute || ss.muteSupported);
+ row.icon.setEnabled(iconEnabled);
+ row.icon.setAlpha(iconEnabled ? 1 : 0.5f);
+ final int iconRes =
+ !isRingAndSuppressed && isRingVibrate ? R.drawable.ic_volume_ringer_vibrate
+ : ss.routedToBluetooth ?
+ (ss.muted ? R.drawable.ic_volume_bt_mute : R.drawable.ic_volume_bt)
+ : isRingAndSuppressed || (mAutomute && ss.level == 0) ? row.iconMuteRes
+ : (ss.muted ? row.iconMuteRes : row.iconRes);
+ if (iconRes != row.cachedIconRes) {
+ if (row.cachedIconRes != 0 && isRingVibrate) {
+ mController.vibrate();
+ }
+ row.cachedIconRes = iconRes;
+ row.icon.setImageResource(iconRes);
+ }
+ row.iconState =
+ iconRes == R.drawable.ic_volume_ringer_vibrate ? Events.ICON_STATE_VIBRATE
+ : (iconRes == R.drawable.ic_volume_bt_mute || iconRes == row.iconMuteRes)
+ ? Events.ICON_STATE_MUTE
+ : (iconRes == R.drawable.ic_volume_bt || iconRes == row.iconRes)
+ ? Events.ICON_STATE_UNMUTE
+ : Events.ICON_STATE_UNKNOWN;
+
+ // update slider
+ updateVolumeRowSliderH(row, isRingAndSuppressed);
+ }
+
+ private void updateVolumeRowSliderH(VolumeRow row, boolean isRingAndSuppressed) {
+ row.slider.setEnabled(!isRingAndSuppressed);
+ if (row.tracking) {
+ return; // don't update if user is sliding
+ }
+ if (isRingAndSuppressed) {
+ row.slider.setProgress(0);
+ return;
+ }
+ final int progress = row.slider.getProgress();
+ final int level = getImpliedLevel(row.slider, progress);
+ final boolean rowVisible = row.view.getVisibility() == View.VISIBLE;
+ final boolean inGracePeriod = (SystemClock.uptimeMillis() - row.userAttempt)
+ < USER_ATTEMPT_GRACE_PERIOD;
+ mHandler.removeMessages(H.RECHECK, row);
+ if (mShowing && rowVisible && inGracePeriod) {
+ if (D.BUG) Log.d(TAG, "inGracePeriod");
+ mHandler.sendMessageAtTime(mHandler.obtainMessage(H.RECHECK, row),
+ row.userAttempt + USER_ATTEMPT_GRACE_PERIOD);
+ return; // don't update if visible and in grace period
+ }
+ final int vlevel = row.ss.muted ? 0 : row.ss.level;
+ if (vlevel == level) {
+ if (mShowing && rowVisible) {
+ return; // don't clamp if visible
+ }
+ }
+ final int newProgress = vlevel * 100;
+ if (progress != newProgress) {
+ if (mShowing && rowVisible) {
+ // animate!
+ if (row.anim != null && row.anim.isRunning()
+ && row.animTargetProgress == newProgress) {
+ return; // already animating to the target progress
+ }
+ // start/update animation
+ if (row.anim == null) {
+ row.anim = ObjectAnimator.ofInt(row.slider, "progress", progress, newProgress);
+ row.anim.setInterpolator(new DecelerateInterpolator());
+ } else {
+ row.anim.cancel();
+ row.anim.setIntValues(progress, newProgress);
+ }
+ row.animTargetProgress = newProgress;
+ row.anim.setDuration(UPDATE_ANIMATION_DURATION);
+ row.anim.start();
+ } else {
+ // update slider directly to clamped value
+ if (row.anim != null) {
+ row.anim.cancel();
+ }
+ row.slider.setProgress(newProgress);
+ }
+ if (mAutomute) {
+ if (vlevel == 0 && !row.ss.muted && row.stream == AudioManager.STREAM_MUSIC) {
+ mController.setStreamMute(row.stream, true);
+ }
+ }
+ }
+ }
+
+ private void recheckH(VolumeRow row) {
+ if (row == null) {
+ if (D.BUG) Log.d(TAG, "recheckH ALL");
+ trimObsoleteH();
+ for (VolumeRow r : mRows) {
+ updateVolumeRowH(r);
+ }
+ } else {
+ if (D.BUG) Log.d(TAG, "recheckH " + row.stream);
+ updateVolumeRowH(row);
+ }
+ }
+
+ private void setStreamImportantH(int stream, boolean important) {
+ for (VolumeRow row : mRows) {
+ if (row.stream == stream) {
+ row.important = important;
+ return;
+ }
+ }
+ }
+
+ private final VolumeDialogController.Callbacks mControllerCallbackH
+ = new VolumeDialogController.Callbacks() {
+ @Override
+ public void onShowRequested(int reason) {
+ showH(reason);
+ }
+
+ @Override
+ public void onDismissRequested(int reason) {
+ dismissH(reason);
+ }
+
+ public void onScreenOff() {
+ dismissH(Events.DISMISS_REASON_SCREEN_OFF);
+ }
+
+ @Override
+ public void onStateChanged(State state) {
+ onStateChangedH(state);
+ }
+
+ @Override
+ public void onLayoutDirectionChanged(int layoutDirection) {
+ mDialogView.setLayoutDirection(layoutDirection);
+ }
+
+ @Override
+ public void onConfigurationChanged() {
+ updateWindowWidthH();
+ mSpTexts.update();
+ }
+
+ @Override
+ public void onShowVibrateHint() {
+ if (mSilentMode) {
+ mController.setRingerMode(AudioManager.RINGER_MODE_SILENT, false);
+ }
+ }
+
+ public void onShowSilentHint() {
+ if (mSilentMode) {
+ mController.setRingerMode(AudioManager.RINGER_MODE_NORMAL, false);
+ }
+ }
+ };
+
+ private final OnClickListener mClickExpand = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mExpanding) return;
+ final boolean newExpand = !mExpanded;
+ Events.writeEvent(Events.EVENT_EXPAND, v);
+ setExpandedH(newExpand);
+ }
+ };
+
+ private final OnClickListener mClickSettings = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mSettingsButton.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ Events.writeEvent(Events.EVENT_SETTINGS_CLICK);
+ onSettingsClickedH();
+ }
+ }, WAIT_FOR_RIPPLE);
+ }
+ };
+
+ private final View.OnClickListener mTurnOffDnd = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mSettingsButton.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ mController.setZenMode(Global.ZEN_MODE_OFF);
+ }
+ }, WAIT_FOR_RIPPLE);
+ }
+ };
+
+ private final ZenFooter.Callback mZenFooterCallback = new ZenFooter.Callback() {
+ @Override
+ public void onFooterExpanded() {
+ mHandler.sendEmptyMessage(H.RESCHEDULE_TIMEOUT);
+ }
+
+ @Override
+ public void onSettingsClicked() {
+ dismiss(Events.DISMISS_REASON_SETTINGS_CLICKED);
+ onZenSettingsClickedH();
+ }
+
+ @Override
+ public void onDoneClicked() {
+ dismiss(Events.DISMISS_REASON_DONE_CLICKED);
+ }
+ };
+
+ private final class H extends Handler {
+ private static final int SHOW = 1;
+ private static final int DISMISS = 2;
+ private static final int RECHECK = 3;
+ private static final int RECHECK_ALL = 4;
+ private static final int SET_STREAM_IMPORTANT = 5;
+ private static final int RESCHEDULE_TIMEOUT = 6;
+
+ public H() {
+ super(Looper.getMainLooper());
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case SHOW: showH(msg.arg1); break;
+ case DISMISS: dismissH(msg.arg1); break;
+ case RECHECK: recheckH((VolumeRow) msg.obj); break;
+ case RECHECK_ALL: recheckH(null); break;
+ case SET_STREAM_IMPORTANT: setStreamImportantH(msg.arg1, msg.arg2 != 0); break;
+ case RESCHEDULE_TIMEOUT: rescheduleTimeoutH(); break;
+ }
+ }
+ }
+
+ private final class CustomDialog extends Dialog {
+ public CustomDialog(Context context) {
+ super(context);
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ rescheduleTimeoutH();
+ return super.dispatchTouchEvent(ev);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ mHandler.sendEmptyMessage(H.RECHECK_ALL);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (isShowing()) {
+ if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
+ dismissH(Events.DISMISS_REASON_TOUCH_OUTSIDE);
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ private final class VolumeSeekBarChangeListener implements OnSeekBarChangeListener {
+ private final VolumeRow mRow;
+
+ private VolumeSeekBarChangeListener(VolumeRow row) {
+ mRow = row;
+ }
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ if (mRow.ss == null) return;
+ if (D.BUG) Log.d(TAG, AudioSystem.streamToString(mRow.stream)
+ + " onProgressChanged " + progress + " fromUser=" + fromUser);
+ if (!fromUser) return;
+ if (mRow.ss.levelMin > 0) {
+ final int minProgress = mRow.ss.levelMin * 100;
+ if (progress < minProgress) {
+ seekBar.setProgress(minProgress);
+ }
+ }
+ final int userLevel = getImpliedLevel(seekBar, progress);
+ if (mRow.ss.level != userLevel || mRow.ss.muted && userLevel > 0) {
+ mRow.userAttempt = SystemClock.uptimeMillis();
+ if (mAutomute) {
+ if (mRow.stream != AudioManager.STREAM_RING) {
+ if (userLevel > 0 && mRow.ss.muted) {
+ mController.setStreamMute(mRow.stream, false);
+ }
+ if (userLevel == 0 && mRow.ss.muteSupported && !mRow.ss.muted) {
+ mController.setStreamMute(mRow.stream, true);
+ }
+ }
+ }
+ if (mRow.requestedLevel != userLevel) {
+ mController.setStreamVolume(mRow.stream, userLevel);
+ mRow.requestedLevel = userLevel;
+ Events.writeEvent(Events.EVENT_TOUCH_LEVEL_CHANGED, mRow.stream, userLevel);
+ }
+ }
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ if (D.BUG) Log.d(TAG, "onStartTrackingTouch"+ " " + mRow.stream);
+ mController.setActiveStream(mRow.stream);
+ mRow.tracking = true;
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ if (D.BUG) Log.d(TAG, "onStopTrackingTouch"+ " " + mRow.stream);
+ mRow.tracking = false;
+ mRow.userAttempt = SystemClock.uptimeMillis();
+ int userLevel = getImpliedLevel(seekBar, seekBar.getProgress());
+ if (mRow.ss.level != userLevel) {
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(H.RECHECK, mRow),
+ USER_ATTEMPT_GRACE_PERIOD);
+ }
+ }
+ }
+
+ private static class VolumeRow {
+ private View view;
+ private View space;
+ private TextView header;
+ private ImageButton icon;
+ private SeekBar slider;
+ private ImageButton settingsButton;
+ private int stream;
+ private StreamState ss;
+ private long userAttempt; // last user-driven slider change
+ private boolean tracking; // tracking slider touch
+ private int requestedLevel;
+ private int iconRes;
+ private int iconMuteRes;
+ private boolean important;
+ private int cachedIconRes;
+ private int iconState; // from Events
+ private boolean cachedShowHeaders = Prefs.DEFAULT_SHOW_HEADERS;
+ private int cachedExpandButtonRes;
+ private ObjectAnimator anim; // slider progress animation for non-touch-related updates
+ private int animTargetProgress;
+ private int lastAudibleLevel = 1;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
new file mode 100644
index 0000000..741e498
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.media.AudioManager;
+import android.media.VolumePolicy;
+import android.os.Bundle;
+import android.os.Handler;
+
+import com.android.systemui.SystemUI;
+import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.qs.tiles.DndTile;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
+import com.android.systemui.statusbar.policy.ZenModeController;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Implementation of VolumeComponent backed by the new volume dialog.
+ */
+public class VolumeDialogComponent implements VolumeComponent {
+ private final SystemUI mSysui;
+ private final Context mContext;
+ private final VolumeDialogController mController;
+ private final ZenModeController mZenModeController;
+ private final VolumeDialog mDialog;
+
+ public VolumeDialogComponent(SystemUI sysui, Context context, Handler handler,
+ ZenModeController zen) {
+ mSysui = sysui;
+ mContext = context;
+ mController = new VolumeDialogController(context, null) {
+ @Override
+ protected void onUserActivityW() {
+ sendUserActivity();
+ }
+ };
+ mZenModeController = zen;
+ mDialog = new VolumeDialog(context, mController, zen) {
+ @Override
+ protected void onZenSettingsClickedH() {
+ startZenSettings();
+ }
+ };
+ applyConfiguration();
+ }
+
+ private void sendUserActivity() {
+ final KeyguardViewMediator kvm = mSysui.getComponent(KeyguardViewMediator.class);
+ if (kvm != null) {
+ kvm.userActivity();
+ }
+ }
+
+ private void applyConfiguration() {
+ mDialog.setStreamImportant(AudioManager.STREAM_ALARM, true);
+ mDialog.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
+ mDialog.setShowHeaders(false);
+ mDialog.setShowFooter(true);
+ mDialog.setZenFooter(true);
+ mDialog.setAutomute(true);
+ mDialog.setSilentMode(false);
+ mController.setVolumePolicy(VolumePolicy.DEFAULT);
+ mController.showDndTile(false);
+ }
+
+ @Override
+ public ZenModeController getZenController() {
+ return mZenModeController;
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ // noop
+ }
+
+ @Override
+ public void dismissNow() {
+ mController.dismiss();
+ }
+
+ @Override
+ public void dispatchDemoCommand(String command, Bundle args) {
+ // noop
+ }
+
+ @Override
+ public void register() {
+ mController.register();
+ DndTile.setCombinedIcon(mContext, true);
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ mController.dump(fd, pw, args);
+ mDialog.dump(pw);
+ }
+
+ private void startZenSettings() {
+ mSysui.getComponent(PhoneStatusBar.class).startActivityDismissingKeyguard(
+ ZenModePanel.ZEN_SETTINGS, true /* onlyProvisioned */, true /* dismissShade */);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
new file mode 100644
index 0000000..ae5312e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
@@ -0,0 +1,962 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume;
+
+import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.database.ContentObserver;
+import android.media.AudioManager;
+import android.media.AudioSystem;
+import android.media.IVolumeController;
+import android.media.VolumePolicy;
+import android.media.session.MediaController.PlaybackInfo;
+import android.media.session.MediaSession.Token;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.Vibrator;
+import android.provider.Settings;
+import android.service.notification.Condition;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.systemui.R;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Source of truth for all state / events related to the volume dialog. No presentation.
+ *
+ * All work done on a dedicated background worker thread & associated worker.
+ *
+ * Methods ending in "W" must be called on the worker thread.
+ */
+public class VolumeDialogController {
+ private static final String TAG = Util.logTag(VolumeDialogController.class);
+
+ private static final int DYNAMIC_STREAM_START_INDEX = 100;
+ private static final int VIBRATE_HINT_DURATION = 50;
+
+ private static final int[] STREAMS = {
+ AudioSystem.STREAM_ALARM,
+ AudioSystem.STREAM_BLUETOOTH_SCO,
+ AudioSystem.STREAM_DTMF,
+ AudioSystem.STREAM_MUSIC,
+ AudioSystem.STREAM_NOTIFICATION,
+ AudioSystem.STREAM_RING,
+ AudioSystem.STREAM_SYSTEM,
+ AudioSystem.STREAM_SYSTEM_ENFORCED,
+ AudioSystem.STREAM_TTS,
+ AudioSystem.STREAM_VOICE_CALL,
+ };
+
+ private final HandlerThread mWorkerThread;
+ private final W mWorker;
+ private final Context mContext;
+ private final AudioManager mAudio;
+ private final NotificationManager mNoMan;
+ private final ComponentName mComponent;
+ private final SettingObserver mObserver;
+ private final Receiver mReceiver = new Receiver();
+ private final MediaSessions mMediaSessions;
+ private final VC mVolumeController = new VC();
+ private final C mCallbacks = new C();
+ private final State mState = new State();
+ private final String[] mStreamTitles;
+ private final MediaSessionsCallbacks mMediaSessionsCallbacksW = new MediaSessionsCallbacks();
+ private final Vibrator mVibrator;
+ private final boolean mHasVibrator;
+
+ private boolean mEnabled;
+ private boolean mDestroyed;
+ private VolumePolicy mVolumePolicy = new VolumePolicy(true, true, false, 400);
+ private boolean mShowDndTile = false;
+
+ public VolumeDialogController(Context context, ComponentName component) {
+ mContext = context.getApplicationContext();
+ Events.writeEvent(Events.EVENT_COLLECTION_STARTED);
+ mComponent = component;
+ mWorkerThread = new HandlerThread(VolumeDialogController.class.getSimpleName());
+ mWorkerThread.start();
+ mWorker = new W(mWorkerThread.getLooper());
+ mMediaSessions = createMediaSessions(mContext, mWorkerThread.getLooper(),
+ mMediaSessionsCallbacksW);
+ mAudio = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ mNoMan = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ mObserver = new SettingObserver(mWorker);
+ mObserver.init();
+ mReceiver.init();
+ mStreamTitles = mContext.getResources().getStringArray(R.array.volume_stream_titles);
+ mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
+ mHasVibrator = mVibrator != null && mVibrator.hasVibrator();
+ }
+
+ public void dismiss() {
+ mCallbacks.onDismissRequested(Events.DISMISS_REASON_VOLUME_CONTROLLER);
+ }
+
+ public void register() {
+ try {
+ mAudio.setVolumeController(mVolumeController);
+ } catch (SecurityException e) {
+ Log.w(TAG, "Unable to set the volume controller", e);
+ return;
+ }
+ setVolumePolicy(mVolumePolicy);
+ showDndTile(mShowDndTile);
+ try {
+ mMediaSessions.init();
+ } catch (SecurityException e) {
+ Log.w(TAG, "No access to media sessions", e);
+ }
+ }
+
+ public void setVolumePolicy(VolumePolicy policy) {
+ mVolumePolicy = policy;
+ try {
+ mAudio.setVolumePolicy(mVolumePolicy);
+ } catch (NoSuchMethodError e) {
+ Log.w(TAG, "No volume policy api");
+ }
+ }
+
+ protected MediaSessions createMediaSessions(Context context, Looper looper,
+ MediaSessions.Callbacks callbacks) {
+ return new MediaSessions(context, looper, callbacks);
+ }
+
+ public void destroy() {
+ if (D.BUG) Log.d(TAG, "destroy");
+ if (mDestroyed) return;
+ mDestroyed = true;
+ Events.writeEvent(Events.EVENT_COLLECTION_STOPPED);
+ mMediaSessions.destroy();
+ mObserver.destroy();
+ mReceiver.destroy();
+ mWorkerThread.quitSafely();
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println(VolumeDialogController.class.getSimpleName() + " state:");
+ pw.print(" mEnabled: "); pw.println(mEnabled);
+ pw.print(" mDestroyed: "); pw.println(mDestroyed);
+ pw.print(" mVolumePolicy: "); pw.println(mVolumePolicy);
+ pw.print(" mEnabled: "); pw.println(mEnabled);
+ pw.print(" mShowDndTile: "); pw.println(mShowDndTile);
+ pw.print(" mHasVibrator: "); pw.println(mHasVibrator);
+ pw.print(" mRemoteStreams: "); pw.println(mMediaSessionsCallbacksW.mRemoteStreams
+ .values());
+ pw.println();
+ mMediaSessions.dump(pw);
+ }
+
+ public void addCallback(Callbacks callback, Handler handler) {
+ mCallbacks.add(callback, handler);
+ }
+
+ public void removeCallback(Callbacks callback) {
+ mCallbacks.remove(callback);
+ }
+
+ public void getState() {
+ if (mDestroyed) return;
+ mWorker.sendEmptyMessage(W.GET_STATE);
+ }
+
+ public void notifyVisible(boolean visible) {
+ if (mDestroyed) return;
+ mWorker.obtainMessage(W.NOTIFY_VISIBLE, visible ? 1 : 0, 0).sendToTarget();
+ }
+
+ public void userActivity() {
+ if (mDestroyed) return;
+ mWorker.removeMessages(W.USER_ACTIVITY);
+ mWorker.sendEmptyMessage(W.USER_ACTIVITY);
+ }
+
+ public void setRingerMode(int value, boolean external) {
+ if (mDestroyed) return;
+ mWorker.obtainMessage(W.SET_RINGER_MODE, value, external ? 1 : 0).sendToTarget();
+ }
+
+ public void setZenMode(int value) {
+ if (mDestroyed) return;
+ mWorker.obtainMessage(W.SET_ZEN_MODE, value, 0).sendToTarget();
+ }
+
+ public void setExitCondition(Condition condition) {
+ if (mDestroyed) return;
+ mWorker.obtainMessage(W.SET_EXIT_CONDITION, condition).sendToTarget();
+ }
+
+ public void setStreamMute(int stream, boolean mute) {
+ if (mDestroyed) return;
+ mWorker.obtainMessage(W.SET_STREAM_MUTE, stream, mute ? 1 : 0).sendToTarget();
+ }
+
+ public void setStreamVolume(int stream, int level) {
+ if (mDestroyed) return;
+ mWorker.obtainMessage(W.SET_STREAM_VOLUME, stream, level).sendToTarget();
+ }
+
+ public void setActiveStream(int stream) {
+ if (mDestroyed) return;
+ mWorker.obtainMessage(W.SET_ACTIVE_STREAM, stream, 0).sendToTarget();
+ }
+
+ public void vibrate() {
+ if (mHasVibrator) {
+ mVibrator.vibrate(VIBRATE_HINT_DURATION);
+ }
+ }
+
+ public boolean hasVibrator() {
+ return mHasVibrator;
+ }
+
+ private void onNotifyVisibleW(boolean visible) {
+ if (mDestroyed) return;
+ mAudio.notifyVolumeControllerVisible(mVolumeController, visible);
+ if (!visible) {
+ if (updateActiveStreamW(-1)) {
+ mCallbacks.onStateChanged(mState);
+ }
+ }
+ }
+
+ protected void onUserActivityW() {
+ // hook for subclasses
+ }
+
+ private boolean checkRoutedToBluetoothW(int stream) {
+ boolean changed = false;
+ if (stream == AudioManager.STREAM_MUSIC) {
+ final boolean routedToBluetooth =
+ (mAudio.getDevicesForStream(AudioManager.STREAM_MUSIC) &
+ (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
+ AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
+ AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0;
+ changed |= updateStreamRoutedToBluetoothW(stream, routedToBluetooth);
+ }
+ return changed;
+ }
+
+ private void onVolumeChangedW(int stream, int flags) {
+ final boolean showUI = (flags & AudioManager.FLAG_SHOW_UI) != 0;
+ final boolean fromKey = (flags & AudioManager.FLAG_FROM_KEY) != 0;
+ final boolean showVibrateHint = (flags & AudioManager.FLAG_SHOW_VIBRATE_HINT) != 0;
+ final boolean showSilentHint = (flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0;
+ boolean changed = false;
+ if (showUI) {
+ changed |= updateActiveStreamW(stream);
+ }
+ changed |= updateStreamLevelW(stream, mAudio.getLastAudibleStreamVolume(stream));
+ changed |= checkRoutedToBluetoothW(showUI ? AudioManager.STREAM_MUSIC : stream);
+ if (changed) {
+ mCallbacks.onStateChanged(mState);
+ }
+ if (showUI) {
+ mCallbacks.onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED);
+ }
+ if (showVibrateHint) {
+ mCallbacks.onShowVibrateHint();
+ }
+ if (showSilentHint) {
+ mCallbacks.onShowSilentHint();
+ }
+ if (changed && fromKey) {
+ Events.writeEvent(Events.EVENT_KEY);
+ }
+ }
+
+ private boolean updateActiveStreamW(int activeStream) {
+ if (activeStream == mState.activeStream) return false;
+ mState.activeStream = activeStream;
+ Events.writeEvent(Events.EVENT_ACTIVE_STREAM_CHANGED, activeStream);
+ if (D.BUG) Log.d(TAG, "updateActiveStreamW " + activeStream);
+ final int s = activeStream < DYNAMIC_STREAM_START_INDEX ? activeStream : -1;
+ if (D.BUG) Log.d(TAG, "forceVolumeControlStream " + s);
+ mAudio.forceVolumeControlStream(s);
+ return true;
+ }
+
+ private StreamState streamStateW(int stream) {
+ StreamState ss = mState.states.get(stream);
+ if (ss == null) {
+ ss = new StreamState();
+ mState.states.put(stream, ss);
+ }
+ return ss;
+ }
+
+ private void onGetStateW() {
+ for (int stream : STREAMS) {
+ updateStreamLevelW(stream, mAudio.getLastAudibleStreamVolume(stream));
+ streamStateW(stream).levelMin = mAudio.getStreamMinVolume(stream);
+ streamStateW(stream).levelMax = mAudio.getStreamMaxVolume(stream);
+ updateStreamMuteW(stream, mAudio.isStreamMute(stream));
+ final StreamState ss = streamStateW(stream);
+ ss.muteSupported = mAudio.isStreamAffectedByMute(stream);
+ ss.name = mStreamTitles[stream];
+ checkRoutedToBluetoothW(stream);
+ }
+ updateRingerModeExternalW(mAudio.getRingerMode());
+ updateZenModeW();
+ updateEffectsSuppressorW(mNoMan.getEffectsSuppressor());
+ updateExitConditionW();
+ mCallbacks.onStateChanged(mState);
+ }
+
+ private boolean updateStreamRoutedToBluetoothW(int stream, boolean routedToBluetooth) {
+ final StreamState ss = streamStateW(stream);
+ if (ss.routedToBluetooth == routedToBluetooth) return false;
+ ss.routedToBluetooth = routedToBluetooth;
+ if (D.BUG) Log.d(TAG, "updateStreamRoutedToBluetoothW stream=" + stream
+ + " routedToBluetooth=" + routedToBluetooth);
+ return true;
+ }
+
+ private boolean updateStreamLevelW(int stream, int level) {
+ final StreamState ss = streamStateW(stream);
+ if (ss.level == level) return false;
+ ss.level = level;
+ if (isLogWorthy(stream)) {
+ Events.writeEvent(Events.EVENT_LEVEL_CHANGED, stream, level);
+ }
+ return true;
+ }
+
+ private static boolean isLogWorthy(int stream) {
+ switch (stream) {
+ case AudioSystem.STREAM_ALARM:
+ case AudioSystem.STREAM_BLUETOOTH_SCO:
+ case AudioSystem.STREAM_MUSIC:
+ case AudioSystem.STREAM_RING:
+ case AudioSystem.STREAM_SYSTEM:
+ case AudioSystem.STREAM_VOICE_CALL:
+ return true;
+ }
+ return false;
+ }
+
+ private boolean updateStreamMuteW(int stream, boolean muted) {
+ final StreamState ss = streamStateW(stream);
+ if (ss.muted == muted) return false;
+ ss.muted = muted;
+ if (isLogWorthy(stream)) {
+ Events.writeEvent(Events.EVENT_MUTE_CHANGED, stream, muted);
+ }
+ if (muted && isRinger(stream)) {
+ updateRingerModeInternalW(mAudio.getRingerModeInternal());
+ }
+ return true;
+ }
+
+ private static boolean isRinger(int stream) {
+ return stream == AudioManager.STREAM_RING || stream == AudioManager.STREAM_NOTIFICATION;
+ }
+
+ private boolean updateExitConditionW() {
+ final Condition exitCondition = mNoMan.getZenModeCondition();
+ if (Objects.equals(mState.exitCondition, exitCondition)) return false;
+ mState.exitCondition = exitCondition;
+ return true;
+ }
+
+ private boolean updateEffectsSuppressorW(ComponentName effectsSuppressor) {
+ if (Objects.equals(mState.effectsSuppressor, effectsSuppressor)) return false;
+ mState.effectsSuppressor = effectsSuppressor;
+ mState.effectsSuppressorName = getApplicationName(mContext, mState.effectsSuppressor);
+ Events.writeEvent(Events.EVENT_SUPPRESSOR_CHANGED, mState.effectsSuppressor,
+ mState.effectsSuppressorName);
+ return true;
+ }
+
+ private static String getApplicationName(Context context, ComponentName component) {
+ if (component == null) return null;
+ final PackageManager pm = context.getPackageManager();
+ final String pkg = component.getPackageName();
+ try {
+ final ApplicationInfo ai = pm.getApplicationInfo(pkg, 0);
+ final String rt = Objects.toString(ai.loadLabel(pm), "").trim();
+ if (rt.length() > 0) {
+ return rt;
+ }
+ } catch (NameNotFoundException e) {}
+ return pkg;
+ }
+
+ private boolean updateZenModeW() {
+ final int zen = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
+ if (mState.zenMode == zen) return false;
+ mState.zenMode = zen;
+ Events.writeEvent(Events.EVENT_ZEN_MODE_CHANGED, zen);
+ return true;
+ }
+
+ private boolean updateRingerModeExternalW(int rm) {
+ if (rm == mState.ringerModeExternal) return false;
+ mState.ringerModeExternal = rm;
+ Events.writeEvent(Events.EVENT_EXTERNAL_RINGER_MODE_CHANGED, rm);
+ return true;
+ }
+
+ private boolean updateRingerModeInternalW(int rm) {
+ if (rm == mState.ringerModeInternal) return false;
+ mState.ringerModeInternal = rm;
+ Events.writeEvent(Events.EVENT_INTERNAL_RINGER_MODE_CHANGED, rm);
+ return true;
+ }
+
+ private void onSetRingerModeW(int mode, boolean external) {
+ if (external) {
+ mAudio.setRingerMode(mode);
+ } else {
+ mAudio.setRingerModeInternal(mode);
+ }
+ }
+
+ private void onSetStreamMuteW(int stream, boolean mute) {
+ mAudio.adjustStreamVolume(stream, mute ? AudioManager.ADJUST_MUTE
+ : AudioManager.ADJUST_UNMUTE, 0);
+ }
+
+ private void onSetStreamVolumeW(int stream, int level) {
+ if (D.BUG) Log.d(TAG, "onSetStreamVolume " + stream + " level=" + level);
+ if (stream >= DYNAMIC_STREAM_START_INDEX) {
+ mMediaSessionsCallbacksW.setStreamVolume(stream, level);
+ return;
+ }
+ mAudio.setStreamVolume(stream, level, 0);
+ }
+
+ private void onSetActiveStreamW(int stream) {
+ boolean changed = updateActiveStreamW(stream);
+ if (changed) {
+ mCallbacks.onStateChanged(mState);
+ }
+ }
+
+ private void onSetExitConditionW(Condition condition) {
+ mNoMan.setZenModeCondition(condition);
+ }
+
+ private void onSetZenModeW(int mode) {
+ if (D.BUG) Log.d(TAG, "onSetZenModeW " + mode);
+ mNoMan.setZenMode(mode);
+ }
+
+ private void onDismissRequestedW(int reason) {
+ mCallbacks.onDismissRequested(reason);
+ }
+
+ public void showDndTile(boolean visible) {
+ if (D.BUG) Log.d(TAG, "showDndTile");
+ mContext.sendBroadcast(new Intent("com.android.systemui.dndtile.SET_VISIBLE")
+ .putExtra("visible", visible));
+ }
+
+ private final class VC extends IVolumeController.Stub {
+ private final String TAG = VolumeDialogController.TAG + ".VC";
+
+ @Override
+ public void displaySafeVolumeWarning(int flags) throws RemoteException {
+ // noop
+ }
+
+ @Override
+ public void volumeChanged(int streamType, int flags) throws RemoteException {
+ if (D.BUG) Log.d(TAG, "volumeChanged " + AudioSystem.streamToString(streamType)
+ + " " + Util.audioManagerFlagsToString(flags));
+ if (mDestroyed) return;
+ mWorker.obtainMessage(W.VOLUME_CHANGED, streamType, flags).sendToTarget();
+ }
+
+ @Override
+ public void masterMuteChanged(int flags) throws RemoteException {
+ if (D.BUG) Log.d(TAG, "masterMuteChanged");
+ }
+
+ @Override
+ public void setLayoutDirection(int layoutDirection) throws RemoteException {
+ if (D.BUG) Log.d(TAG, "setLayoutDirection");
+ if (mDestroyed) return;
+ mWorker.obtainMessage(W.LAYOUT_DIRECTION_CHANGED, layoutDirection, 0).sendToTarget();
+ }
+
+ @Override
+ public void dismiss() throws RemoteException {
+ if (D.BUG) Log.d(TAG, "dismiss requested");
+ if (mDestroyed) return;
+ mWorker.obtainMessage(W.DISMISS_REQUESTED, Events.DISMISS_REASON_VOLUME_CONTROLLER, 0)
+ .sendToTarget();
+ mWorker.sendEmptyMessage(W.DISMISS_REQUESTED);
+ }
+ }
+
+ private final class W extends Handler {
+ private static final int VOLUME_CHANGED = 1;
+ private static final int DISMISS_REQUESTED = 2;
+ private static final int GET_STATE = 3;
+ private static final int SET_RINGER_MODE = 4;
+ private static final int SET_ZEN_MODE = 5;
+ private static final int SET_EXIT_CONDITION = 6;
+ private static final int SET_STREAM_MUTE = 7;
+ private static final int LAYOUT_DIRECTION_CHANGED = 8;
+ private static final int CONFIGURATION_CHANGED = 9;
+ private static final int SET_STREAM_VOLUME = 10;
+ private static final int SET_ACTIVE_STREAM = 11;
+ private static final int NOTIFY_VISIBLE = 12;
+ private static final int USER_ACTIVITY = 13;
+
+ W(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case VOLUME_CHANGED: onVolumeChangedW(msg.arg1, msg.arg2); break;
+ case DISMISS_REQUESTED: onDismissRequestedW(msg.arg1); break;
+ case GET_STATE: onGetStateW(); break;
+ case SET_RINGER_MODE: onSetRingerModeW(msg.arg1, msg.arg2 != 0); break;
+ case SET_ZEN_MODE: onSetZenModeW(msg.arg1); break;
+ case SET_EXIT_CONDITION: onSetExitConditionW((Condition) msg.obj); break;
+ case SET_STREAM_MUTE: onSetStreamMuteW(msg.arg1, msg.arg2 != 0); break;
+ case LAYOUT_DIRECTION_CHANGED: mCallbacks.onLayoutDirectionChanged(msg.arg1); break;
+ case CONFIGURATION_CHANGED: mCallbacks.onConfigurationChanged(); break;
+ case SET_STREAM_VOLUME: onSetStreamVolumeW(msg.arg1, msg.arg2); break;
+ case SET_ACTIVE_STREAM: onSetActiveStreamW(msg.arg1); break;
+ case NOTIFY_VISIBLE: onNotifyVisibleW(msg.arg1 != 0);
+ case USER_ACTIVITY: onUserActivityW();
+ }
+ }
+ }
+
+ private final class C implements Callbacks {
+ private final HashMap<Callbacks, Handler> mCallbackMap = new HashMap<>();
+
+ public void add(Callbacks callback, Handler handler) {
+ if (callback == null || handler == null) throw new IllegalArgumentException();
+ mCallbackMap.put(callback, handler);
+ }
+
+ public void remove(Callbacks callback) {
+ mCallbackMap.remove(callback);
+ }
+
+ @Override
+ public void onShowRequested(final int reason) {
+ for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
+ entry.getValue().post(new Runnable() {
+ @Override
+ public void run() {
+ entry.getKey().onShowRequested(reason);
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onDismissRequested(final int reason) {
+ for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
+ entry.getValue().post(new Runnable() {
+ @Override
+ public void run() {
+ entry.getKey().onDismissRequested(reason);
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onStateChanged(final State state) {
+ final long time = System.currentTimeMillis();
+ final State copy = state.copy();
+ for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
+ entry.getValue().post(new Runnable() {
+ @Override
+ public void run() {
+ entry.getKey().onStateChanged(copy);
+ }
+ });
+ }
+ Events.writeState(time, copy);
+ }
+
+ @Override
+ public void onLayoutDirectionChanged(final int layoutDirection) {
+ for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
+ entry.getValue().post(new Runnable() {
+ @Override
+ public void run() {
+ entry.getKey().onLayoutDirectionChanged(layoutDirection);
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onConfigurationChanged() {
+ for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
+ entry.getValue().post(new Runnable() {
+ @Override
+ public void run() {
+ entry.getKey().onConfigurationChanged();
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onShowVibrateHint() {
+ for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
+ entry.getValue().post(new Runnable() {
+ @Override
+ public void run() {
+ entry.getKey().onShowVibrateHint();
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onShowSilentHint() {
+ for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
+ entry.getValue().post(new Runnable() {
+ @Override
+ public void run() {
+ entry.getKey().onShowSilentHint();
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onScreenOff() {
+ for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
+ entry.getValue().post(new Runnable() {
+ @Override
+ public void run() {
+ entry.getKey().onScreenOff();
+ }
+ });
+ }
+ }
+ }
+
+
+ private final class SettingObserver extends ContentObserver {
+ private final Uri SERVICE_URI = Settings.Secure.getUriFor(
+ Settings.Secure.VOLUME_CONTROLLER_SERVICE_COMPONENT);
+ private final Uri ZEN_MODE_URI =
+ Settings.Global.getUriFor(Settings.Global.ZEN_MODE);
+ private final Uri ZEN_MODE_CONFIG_URI =
+ Settings.Global.getUriFor(Settings.Global.ZEN_MODE_CONFIG_ETAG);
+
+ public SettingObserver(Handler handler) {
+ super(handler);
+ }
+
+ public void init() {
+ mContext.getContentResolver().registerContentObserver(SERVICE_URI, false, this);
+ mContext.getContentResolver().registerContentObserver(ZEN_MODE_URI, false, this);
+ mContext.getContentResolver().registerContentObserver(ZEN_MODE_CONFIG_URI, false, this);
+ onChange(true, SERVICE_URI);
+ }
+
+ public void destroy() {
+ mContext.getContentResolver().unregisterContentObserver(this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ boolean changed = false;
+ if (SERVICE_URI.equals(uri)) {
+ final String setting = Settings.Secure.getString(mContext.getContentResolver(),
+ Settings.Secure.VOLUME_CONTROLLER_SERVICE_COMPONENT);
+ final boolean enabled = setting != null && mComponent != null
+ && mComponent.equals(ComponentName.unflattenFromString(setting));
+ if (enabled == mEnabled) return;
+ if (enabled) {
+ register();
+ }
+ mEnabled = enabled;
+ }
+ if (ZEN_MODE_URI.equals(uri)) {
+ changed = updateZenModeW();
+ }
+ if (ZEN_MODE_CONFIG_URI.equals(uri)) {
+ changed = updateExitConditionW();
+ }
+ if (changed) {
+ mCallbacks.onStateChanged(mState);
+ }
+ }
+ }
+
+ private final class Receiver extends BroadcastReceiver {
+
+ public void init() {
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
+ filter.addAction(AudioManager.STREAM_DEVICES_CHANGED_ACTION);
+ filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
+ filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
+ filter.addAction(AudioManager.STREAM_MUTE_CHANGED_ACTION);
+ filter.addAction(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
+ filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ mContext.registerReceiver(this, filter, null, mWorker);
+ }
+
+ public void destroy() {
+ mContext.unregisterReceiver(this);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ boolean changed = false;
+ if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) {
+ final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
+ final int level = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
+ final int oldLevel = intent
+ .getIntExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, -1);
+ if (D.BUG) Log.d(TAG, "onReceive VOLUME_CHANGED_ACTION stream=" + stream
+ + " level=" + level + " oldLevel=" + oldLevel);
+ changed = updateStreamLevelW(stream, level);
+ } else if (action.equals(AudioManager.STREAM_DEVICES_CHANGED_ACTION)) {
+ final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
+ final int devices = intent
+ .getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_DEVICES, -1);
+ final int oldDevices = intent
+ .getIntExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_DEVICES, -1);
+ if (D.BUG) Log.d(TAG, "onReceive STREAM_DEVICES_CHANGED_ACTION stream="
+ + stream + " devices=" + devices + " oldDevices=" + oldDevices);
+ changed = checkRoutedToBluetoothW(stream);
+ } else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
+ final int rm = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1);
+ if (D.BUG) Log.d(TAG, "onReceive RINGER_MODE_CHANGED_ACTION rm="
+ + Util.ringerModeToString(rm));
+ changed = updateRingerModeExternalW(rm);
+ } else if (action.equals(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION)) {
+ final int rm = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1);
+ if (D.BUG) Log.d(TAG, "onReceive INTERNAL_RINGER_MODE_CHANGED_ACTION rm="
+ + Util.ringerModeToString(rm));
+ changed = updateRingerModeInternalW(rm);
+ } else if (action.equals(AudioManager.STREAM_MUTE_CHANGED_ACTION)) {
+ final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
+ final boolean muted = intent
+ .getBooleanExtra(AudioManager.EXTRA_STREAM_VOLUME_MUTED, false);
+ if (D.BUG) Log.d(TAG, "onReceive STREAM_MUTE_CHANGED_ACTION stream=" + stream
+ + " muted=" + muted);
+ changed = updateStreamMuteW(stream, muted);
+ } else if (action.equals(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED)) {
+ if (D.BUG) Log.d(TAG, "onReceive ACTION_EFFECTS_SUPPRESSOR_CHANGED");
+ changed = updateEffectsSuppressorW(mNoMan.getEffectsSuppressor());
+ } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
+ if (D.BUG) Log.d(TAG, "onReceive ACTION_CONFIGURATION_CHANGED");
+ mCallbacks.onConfigurationChanged();
+ } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
+ if (D.BUG) Log.d(TAG, "onReceive ACTION_SCREEN_OFF");
+ mCallbacks.onScreenOff();
+ }
+ if (changed) {
+ mCallbacks.onStateChanged(mState);
+ }
+ }
+ }
+
+ private final class MediaSessionsCallbacks implements MediaSessions.Callbacks {
+ private final HashMap<Token, Integer> mRemoteStreams = new HashMap<>();
+
+ private int mNextStream = DYNAMIC_STREAM_START_INDEX;
+
+ @Override
+ public void onRemoteUpdate(Token token, String name, PlaybackInfo pi) {
+ if (!mRemoteStreams.containsKey(token)) {
+ mRemoteStreams.put(token, mNextStream);
+ if (D.BUG) Log.d(TAG, "onRemoteUpdate: " + name + " is stream " + mNextStream);
+ mNextStream++;
+ }
+ final int stream = mRemoteStreams.get(token);
+ boolean changed = mState.states.indexOfKey(stream) < 0;
+ final StreamState ss = streamStateW(stream);
+ ss.dynamic = true;
+ ss.levelMin = 0;
+ ss.levelMax = pi.getMaxVolume();
+ if (ss.level != pi.getCurrentVolume()) {
+ ss.level = pi.getCurrentVolume();
+ changed = true;
+ }
+ if (!Objects.equals(ss.name, name)) {
+ ss.name = name;
+ changed = true;
+ }
+ if (changed) {
+ if (D.BUG) Log.d(TAG, "onRemoteUpdate: " + name + ": " + ss.level
+ + " of " + ss.levelMax);
+ mCallbacks.onStateChanged(mState);
+ }
+ }
+
+ @Override
+ public void onRemoteVolumeChanged(Token token, int flags) {
+ final int stream = mRemoteStreams.get(token);
+ final boolean showUI = (flags & AudioManager.FLAG_SHOW_UI) != 0;
+ boolean changed = updateActiveStreamW(stream);
+ if (showUI) {
+ changed |= checkRoutedToBluetoothW(AudioManager.STREAM_MUSIC);
+ }
+ if (changed) {
+ mCallbacks.onStateChanged(mState);
+ }
+ if (showUI) {
+ mCallbacks.onShowRequested(Events.SHOW_REASON_REMOTE_VOLUME_CHANGED);
+ }
+ }
+
+ @Override
+ public void onRemoteRemoved(Token token) {
+ final int stream = mRemoteStreams.get(token);
+ mState.states.remove(stream);
+ if (mState.activeStream == stream) {
+ updateActiveStreamW(-1);
+ }
+ mCallbacks.onStateChanged(mState);
+ }
+
+ public void setStreamVolume(int stream, int level) {
+ final Token t = findToken(stream);
+ if (t == null) {
+ Log.w(TAG, "setStreamVolume: No token found for stream: " + stream);
+ return;
+ }
+ mMediaSessions.setVolume(t, level);
+ }
+
+ private Token findToken(int stream) {
+ for (Map.Entry<Token, Integer> entry : mRemoteStreams.entrySet()) {
+ if (entry.getValue().equals(stream)) {
+ return entry.getKey();
+ }
+ }
+ return null;
+ }
+ }
+
+ public static final class StreamState {
+ public boolean dynamic;
+ public int level;
+ public int levelMin;
+ public int levelMax;
+ public boolean muted;
+ public boolean muteSupported;
+ public String name;
+ public boolean routedToBluetooth;
+
+ public StreamState copy() {
+ final StreamState rt = new StreamState();
+ rt.dynamic = dynamic;
+ rt.level = level;
+ rt.levelMin = levelMin;
+ rt.levelMax = levelMax;
+ rt.muted = muted;
+ rt.muteSupported = muteSupported;
+ rt.name = name;
+ rt.routedToBluetooth = routedToBluetooth;
+ return rt;
+ }
+ }
+
+ public static final class State {
+ public static int NO_ACTIVE_STREAM = -1;
+
+ public final SparseArray<StreamState> states = new SparseArray<StreamState>();
+
+ public int ringerModeInternal;
+ public int ringerModeExternal;
+ public int zenMode;
+ public ComponentName effectsSuppressor;
+ public String effectsSuppressorName;
+ public Condition exitCondition;
+ public int activeStream = NO_ACTIVE_STREAM;
+
+ public State copy() {
+ final State rt = new State();
+ for (int i = 0; i < states.size(); i++) {
+ rt.states.put(states.keyAt(i), states.valueAt(i).copy());
+ }
+ rt.ringerModeExternal = ringerModeExternal;
+ rt.ringerModeInternal = ringerModeInternal;
+ rt.zenMode = zenMode;
+ if (effectsSuppressor != null) rt.effectsSuppressor = effectsSuppressor.clone();
+ rt.effectsSuppressorName = effectsSuppressorName;
+ if (exitCondition != null) rt.exitCondition = exitCondition.copy();
+ rt.activeStream = activeStream;
+ return rt;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("{");
+ for (int i = 0; i < states.size(); i++) {
+ if (i > 0) sb.append(',');
+ final int stream = states.keyAt(i);
+ final StreamState ss = states.valueAt(i);
+ sb.append(AudioSystem.streamToString(stream)).append(":").append(ss.level)
+ .append("[").append(ss.levelMin).append("..").append(ss.levelMax);
+ if (ss.muted) sb.append(" [MUTED]");
+ }
+ sb.append(",ringerModeExternal:").append(ringerModeExternal);
+ sb.append(",ringerModeInternal:").append(ringerModeInternal);
+ sb.append(",zenMode:").append(zenMode);
+ sb.append(",effectsSuppressor:").append(effectsSuppressor);
+ sb.append(",effectsSuppressorName:").append(effectsSuppressorName);
+ sb.append(",exitCondition:").append(exitCondition);
+ sb.append(",activeStream:").append(activeStream);
+ return sb.append('}').toString();
+ }
+ }
+
+ public interface Callbacks {
+ void onShowRequested(int reason);
+ void onDismissRequested(int reason);
+ void onStateChanged(State state);
+ void onLayoutDirectionChanged(int layoutDirection);
+ void onConfigurationChanged();
+ void onShowVibrateHint();
+ void onShowSilentHint();
+ void onScreenOff();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
index d16b818..50d5f7a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
@@ -384,7 +384,7 @@ public class VolumePanel extends Handler implements DemoMode {
final Window window = mDialog.getWindow();
window.requestFeature(Window.FEATURE_NO_TITLE);
mDialog.setCanceledOnTouchOutside(true);
- mDialog.setContentView(com.android.systemui.R.layout.volume_dialog);
+ mDialog.setContentView(com.android.systemui.R.layout.volume_panel_dialog);
mDialog.setOnDismissListener(new OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanelComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanelComponent.java
new file mode 100644
index 0000000..fa6ea9e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanelComponent.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.media.AudioManager;
+import android.media.IRemoteVolumeController;
+import android.media.IVolumeController;
+import android.media.VolumePolicy;
+import android.media.session.ISessionController;
+import android.media.session.MediaController;
+import android.media.session.MediaSessionManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.RemoteException;
+
+import com.android.systemui.R;
+import com.android.systemui.SystemUI;
+import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.qs.tiles.DndTile;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
+import com.android.systemui.statusbar.policy.ZenModeController;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Implementation of VolumeComponent backed by the old volume panel.
+ */
+public class VolumePanelComponent implements VolumeComponent {
+
+ private final SystemUI mSysui;
+ private final Context mContext;
+ private final Handler mHandler;
+ private final VolumeController mVolumeController;
+ private final RemoteVolumeController mRemoteVolumeController;
+ private final AudioManager mAudioManager;
+ private final MediaSessionManager mMediaSessionManager;
+
+ private VolumePanel mPanel;
+ private int mDismissDelay;
+
+ public VolumePanelComponent(SystemUI sysui, Context context, Handler handler,
+ ZenModeController controller) {
+ mSysui = sysui;
+ mContext = context;
+ mHandler = handler;
+ mAudioManager = context.getSystemService(AudioManager.class);
+ mMediaSessionManager = context.getSystemService(MediaSessionManager.class);
+ mVolumeController = new VolumeController();
+ mRemoteVolumeController = new RemoteVolumeController();
+ mDismissDelay = mContext.getResources().getInteger(R.integer.volume_panel_dismiss_delay);
+ mPanel = new VolumePanel(mContext, controller);
+ mPanel.setCallback(new VolumePanel.Callback() {
+ @Override
+ public void onZenSettings() {
+ mHandler.removeCallbacks(mStartZenSettings);
+ mHandler.post(mStartZenSettings);
+ }
+
+ @Override
+ public void onInteraction() {
+ final KeyguardViewMediator kvm = mSysui.getComponent(KeyguardViewMediator.class);
+ if (kvm != null) {
+ kvm.userActivity();
+ }
+ }
+
+ @Override
+ public void onVisible(boolean visible) {
+ if (mAudioManager != null && mVolumeController != null) {
+ mAudioManager.notifyVolumeControllerVisible(mVolumeController, visible);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mPanel != null) {
+ mPanel.dump(fd, pw, args);
+ }
+ }
+
+ public void register() {
+ mAudioManager.setVolumeController(mVolumeController);
+ mAudioManager.setVolumePolicy(VolumePolicy.DEFAULT);
+ mMediaSessionManager.setRemoteVolumeController(mRemoteVolumeController);
+ DndTile.setVisible(mContext, false);
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ if (mPanel != null) {
+ mPanel.onConfigurationChanged(newConfig);
+ }
+ }
+
+ @Override
+ public ZenModeController getZenController() {
+ return mPanel.getZenController();
+ }
+
+ @Override
+ public void dispatchDemoCommand(String command, Bundle args) {
+ mPanel.dispatchDemoCommand(command, args);
+ }
+
+ @Override
+ public void dismissNow() {
+ mPanel.postDismiss(0);
+ }
+
+ private final Runnable mStartZenSettings = new Runnable() {
+ @Override
+ public void run() {
+ mSysui.getComponent(PhoneStatusBar.class).startActivityDismissingKeyguard(
+ ZenModePanel.ZEN_SETTINGS, true /* onlyProvisioned */, true /* dismissShade */);
+ mPanel.postDismiss(mDismissDelay);
+ }
+ };
+
+ private final class RemoteVolumeController extends IRemoteVolumeController.Stub {
+ @Override
+ public void remoteVolumeChanged(ISessionController binder, int flags)
+ throws RemoteException {
+ MediaController controller = new MediaController(mContext, binder);
+ mPanel.postRemoteVolumeChanged(controller, flags);
+ }
+
+ @Override
+ public void updateRemoteController(ISessionController session) throws RemoteException {
+ mPanel.postRemoteSliderVisibility(session != null);
+ // TODO stash default session in case the slider can be opened other
+ // than by remoteVolumeChanged.
+ }
+ }
+
+ /** For now, simply host an unmodified base volume panel in this process. */
+ private final class VolumeController extends IVolumeController.Stub {
+
+ @Override
+ public void displaySafeVolumeWarning(int flags) throws RemoteException {
+ mPanel.postDisplaySafeVolumeWarning(flags);
+ }
+
+ @Override
+ public void volumeChanged(int streamType, int flags)
+ throws RemoteException {
+ mPanel.postVolumeChanged(streamType, flags);
+ }
+
+ @Override
+ public void masterMuteChanged(int flags) throws RemoteException {
+ // no-op
+ }
+
+ @Override
+ public void setLayoutDirection(int layoutDirection)
+ throws RemoteException {
+ mPanel.postLayoutDirection(layoutDirection);
+ }
+
+ @Override
+ public void dismiss() throws RemoteException {
+ dismissNow();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index ac08904..387aed0 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -29,25 +29,17 @@ import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
import android.media.AudioManager;
-import android.media.IRemoteVolumeController;
-import android.media.IVolumeController;
-import android.media.VolumePolicy;
-import android.media.session.ISessionController;
-import android.media.session.MediaController;
import android.media.session.MediaSessionManager;
-import android.os.Bundle;
import android.os.Handler;
-import android.os.RemoteException;
+import android.os.SystemProperties;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
-import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.qs.tiles.DndTile;
import com.android.systemui.statusbar.ServiceMonitor;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
@@ -59,6 +51,8 @@ public class VolumeUI extends SystemUI {
private static final String TAG = "VolumeUI";
private static boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
+ private static final boolean USE_OLD_VOLUME = SystemProperties.getBoolean("volume.old", false);
+
private final Handler mHandler = new Handler();
private final Receiver mReceiver = new Receiver();
private final RestorationNotification mRestorationNotification = new RestorationNotification();
@@ -67,12 +61,10 @@ public class VolumeUI extends SystemUI {
private AudioManager mAudioManager;
private NotificationManager mNotificationManager;
private MediaSessionManager mMediaSessionManager;
- private VolumeController mVolumeController;
- private RemoteVolumeController mRemoteVolumeController;
private ServiceMonitor mVolumeControllerService;
- private VolumePanel mPanel;
- private int mDismissDelay;
+ private VolumePanelComponent mOldVolume;
+ private VolumeDialogComponent mNewVolume;
@Override
public void start() {
@@ -83,10 +75,10 @@ public class VolumeUI extends SystemUI {
(NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
mMediaSessionManager = (MediaSessionManager) mContext
.getSystemService(Context.MEDIA_SESSION_SERVICE);
- initPanel();
- mVolumeController = new VolumeController();
- mRemoteVolumeController = new RemoteVolumeController();
- putComponent(VolumeComponent.class, mVolumeController);
+ final ZenModeController zenController = new ZenModeControllerImpl(mContext, mHandler);
+ mOldVolume = new VolumePanelComponent(this, mContext, mHandler, zenController);
+ mNewVolume = new VolumeDialogComponent(this, mContext, null, zenController);
+ putComponent(VolumeComponent.class, getVolumeComponent());
mReceiver.start();
mVolumeControllerService = new ServiceMonitor(TAG, LOGD,
mContext, Settings.Secure.VOLUME_CONTROLLER_SERVICE_COMPONENT,
@@ -94,30 +86,30 @@ public class VolumeUI extends SystemUI {
mVolumeControllerService.start();
}
+ private VolumeComponent getVolumeComponent() {
+ return USE_OLD_VOLUME ? mOldVolume : mNewVolume;
+ }
+
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- if (mPanel != null) {
- mPanel.onConfigurationChanged(newConfig);
- }
+ if (!mEnabled) return;
+ getVolumeComponent().onConfigurationChanged(newConfig);
}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.print("mEnabled="); pw.println(mEnabled);
+ if (!mEnabled) return;
pw.print("mVolumeControllerService="); pw.println(mVolumeControllerService.getComponent());
- if (mPanel != null) {
- mPanel.dump(fd, pw, args);
- }
+ getVolumeComponent().dump(fd, pw, args);
}
- private void setVolumeController(boolean register) {
+ private void setDefaultVolumeController(boolean register) {
if (register) {
- if (LOGD) Log.d(TAG, "Registering default volume controller");
- mAudioManager.setVolumeController(mVolumeController);
- mAudioManager.setVolumePolicy(VolumePolicy.DEFAULT);
- mMediaSessionManager.setRemoteVolumeController(mRemoteVolumeController);
DndTile.setVisible(mContext, false);
+ if (LOGD) Log.d(TAG, "Registering default volume controller");
+ getVolumeComponent().register();
} else {
if (LOGD) Log.d(TAG, "Unregistering default volume controller");
mAudioManager.setVolumeController(null);
@@ -125,33 +117,6 @@ public class VolumeUI extends SystemUI {
}
}
- private void initPanel() {
- mDismissDelay = mContext.getResources().getInteger(R.integer.volume_panel_dismiss_delay);
- mPanel = new VolumePanel(mContext, new ZenModeControllerImpl(mContext, mHandler));
- mPanel.setCallback(new VolumePanel.Callback() {
- @Override
- public void onZenSettings() {
- mHandler.removeCallbacks(mStartZenSettings);
- mHandler.post(mStartZenSettings);
- }
-
- @Override
- public void onInteraction() {
- final KeyguardViewMediator kvm = getComponent(KeyguardViewMediator.class);
- if (kvm != null) {
- kvm.userActivity();
- }
- }
-
- @Override
- public void onVisible(boolean visible) {
- if (mAudioManager != null && mVolumeController != null) {
- mAudioManager.notifyVolumeControllerVisible(mVolumeController, visible);
- }
- }
- });
- }
-
private String getAppLabel(ComponentName component) {
final String pkg = component.getPackageName();
try {
@@ -179,83 +144,11 @@ public class VolumeUI extends SystemUI {
d.show();
}
- private final Runnable mStartZenSettings = new Runnable() {
- @Override
- public void run() {
- getComponent(PhoneStatusBar.class).startActivityDismissingKeyguard(
- ZenModePanel.ZEN_SETTINGS, true /* onlyProvisioned */, true /* dismissShade */);
- mPanel.postDismiss(mDismissDelay);
- }
- };
-
- /** For now, simply host an unmodified base volume panel in this process. */
- private final class VolumeController extends IVolumeController.Stub implements VolumeComponent {
-
- @Override
- public void displaySafeVolumeWarning(int flags) throws RemoteException {
- mPanel.postDisplaySafeVolumeWarning(flags);
- }
-
- @Override
- public void volumeChanged(int streamType, int flags)
- throws RemoteException {
- mPanel.postVolumeChanged(streamType, flags);
- }
-
- @Override
- public void masterMuteChanged(int flags) throws RemoteException {
- // no-op
- }
-
- @Override
- public void setLayoutDirection(int layoutDirection)
- throws RemoteException {
- mPanel.postLayoutDirection(layoutDirection);
- }
-
- @Override
- public void dismiss() throws RemoteException {
- dismissNow();
- }
-
- @Override
- public ZenModeController getZenController() {
- return mPanel.getZenController();
- }
-
- @Override
- public void dispatchDemoCommand(String command, Bundle args) {
- mPanel.dispatchDemoCommand(command, args);
- }
-
- @Override
- public void dismissNow() {
- mPanel.postDismiss(0);
- }
- }
-
- private final class RemoteVolumeController extends IRemoteVolumeController.Stub {
-
- @Override
- public void remoteVolumeChanged(ISessionController binder, int flags)
- throws RemoteException {
- MediaController controller = new MediaController(mContext, binder);
- mPanel.postRemoteVolumeChanged(controller, flags);
- }
-
- @Override
- public void updateRemoteController(ISessionController session) throws RemoteException {
- mPanel.postRemoteSliderVisibility(session != null);
- // TODO stash default session in case the slider can be opened other
- // than by remoteVolumeChanged.
- }
- }
-
private final class ServiceMonitorCallbacks implements ServiceMonitor.Callbacks {
@Override
public void onNoService() {
if (LOGD) Log.d(TAG, "onNoService");
- setVolumeController(true);
+ setDefaultVolumeController(true);
mRestorationNotification.hide();
if (!mVolumeControllerService.isPackageAvailable()) {
mVolumeControllerService.setComponent(null);
@@ -267,8 +160,8 @@ public class VolumeUI extends SystemUI {
if (LOGD) Log.d(TAG, "onServiceStartAttempt");
// poke the setting to update the uid
mVolumeControllerService.setComponent(mVolumeControllerService.getComponent());
- setVolumeController(false);
- mVolumeController.dismissNow();
+ setDefaultVolumeController(false);
+ getVolumeComponent().dismissNow();
mRestorationNotification.show();
return 0;
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
new file mode 100644
index 0000000..ba5b8d1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.volume;
+
+import android.animation.LayoutTransition;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.provider.Settings.Global;
+import android.service.notification.Condition;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.LinearLayout;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.ZenModeController;
+
+/**
+ * Switch bar + zen mode panel (conditions) attached to the bottom of the volume dialog.
+ */
+public class ZenFooter extends LinearLayout {
+ private static final String TAG = Util.logTag(ZenFooter.class);
+
+ private final Context mContext;
+ private final float mSecondaryAlpha;
+ private final LayoutTransition mLayoutTransition;
+
+ private ZenModeController mController;
+ private Switch mSwitch;
+ private ZenModePanel mZenModePanel;
+ private View mZenModePanelButtons;
+ private View mZenModePanelMoreButton;
+ private View mZenModePanelDoneButton;
+ private View mSwitchBar;
+ private View mSwitchBarIcon;
+ private View mSummary;
+ private TextView mSummaryLine1;
+ private TextView mSummaryLine2;
+ private boolean mFooterExpanded;
+ private int mZen = -1;
+ private Callback mCallback;
+
+ public ZenFooter(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mContext = context;
+ mSecondaryAlpha = getFloat(context.getResources(), R.dimen.volume_secondary_alpha);
+ mLayoutTransition = new LayoutTransition();
+ mLayoutTransition.setDuration(new ValueAnimator().getDuration() / 2);
+ mLayoutTransition.disableTransitionType(LayoutTransition.DISAPPEARING);
+ mLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
+ }
+
+ private static float getFloat(Resources r, int resId) {
+ final TypedValue tv = new TypedValue();
+ r.getValue(resId, tv, true);
+ return tv.getFloat();
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mSwitchBar = findViewById(R.id.volume_zen_switch_bar);
+ mSwitchBarIcon = findViewById(R.id.volume_zen_switch_bar_icon);
+ mSwitch = (Switch) findViewById(R.id.volume_zen_switch);
+ mZenModePanel = (ZenModePanel) findViewById(R.id.zen_mode_panel);
+ mZenModePanelButtons = findViewById(R.id.volume_zen_mode_panel_buttons);
+ mZenModePanelMoreButton = findViewById(R.id.volume_zen_mode_panel_more);
+ mZenModePanelDoneButton = findViewById(R.id.volume_zen_mode_panel_done);
+ mSummary = findViewById(R.id.volume_zen_panel_summary);
+ mSummaryLine1 = (TextView) findViewById(R.id.volume_zen_panel_summary_line_1);
+ mSummaryLine2 = (TextView) findViewById(R.id.volume_zen_panel_summary_line_2);
+ }
+
+ public void init(ZenModeController controller, Callback callback) {
+ mCallback = callback;
+ mController = controller;
+ mZenModePanel.init(controller);
+ mZenModePanel.setEmbedded(true);
+ mSwitch.setOnCheckedChangeListener(mCheckedListener);
+ mController.addCallback(new ZenModeController.Callback() {
+ @Override
+ public void onZenChanged(int zen) {
+ setZen(zen);
+ }
+ @Override
+ public void onExitConditionChanged(Condition exitCondition) {
+ update();
+ }
+ });
+ mSwitchBar.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mSwitch.setChecked(!mSwitch.isChecked());
+ }
+ });
+ mZenModePanelMoreButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mCallback != null) {
+ mCallback.onSettingsClicked();
+ }
+ }
+ });
+ mZenModePanelDoneButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mCallback != null) {
+ mCallback.onDoneClicked();
+ }
+ }
+ });
+ mZen = mController.getZen();
+ update();
+ }
+
+ private void setZen(int zen) {
+ if (mZen == zen) return;
+ mZen = zen;
+ update();
+ }
+
+ public boolean isZen() {
+ return isZenPriority() || isZenNone();
+ }
+
+ private boolean isZenPriority() {
+ return mZen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ }
+
+ private boolean isZenNone() {
+ return mZen == Global.ZEN_MODE_NO_INTERRUPTIONS;
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ setLayoutTransition(null);
+ setFooterExpanded(false);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ setLayoutTransition(mLayoutTransition);
+ }
+
+ private boolean setFooterExpanded(boolean expanded) {
+ if (mFooterExpanded == expanded) return false;
+ mFooterExpanded = expanded;
+ update();
+ if (mCallback != null) {
+ mCallback.onFooterExpanded();
+ }
+ return true;
+ }
+
+ public boolean isFooterExpanded() {
+ return mFooterExpanded;
+ }
+
+ public void update() {
+ final boolean isZen = isZen();
+ mSwitch.setOnCheckedChangeListener(null);
+ mSwitch.setChecked(isZen);
+ mSwitch.setOnCheckedChangeListener(mCheckedListener);
+ Util.setVisOrGone(mZenModePanel, isZen && mFooterExpanded);
+ Util.setVisOrGone(mZenModePanelButtons, isZen && mFooterExpanded);
+ Util.setVisOrGone(mSummary, isZen && !mFooterExpanded);
+ mSwitchBarIcon.setAlpha(isZen ? 1 : mSecondaryAlpha);
+ final String line1 =
+ isZenPriority() ? mContext.getString(R.string.interruption_level_priority)
+ : isZenNone() ? mContext.getString(R.string.interruption_level_none)
+ : null;
+ Util.setText(mSummaryLine1, line1);
+ Util.setText(mSummaryLine2, mZenModePanel.getExitConditionText());
+ }
+
+ private final OnCheckedChangeListener mCheckedListener = new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (D.BUG) Log.d(TAG, "onCheckedChanged " + isChecked);
+ if (isChecked != isZen()) {
+ final int newZen = isChecked ? Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+ : Global.ZEN_MODE_OFF;
+ mZen = newZen; // this one's optimistic
+ setFooterExpanded(isChecked);
+ mController.setZen(newZen);
+ }
+ }
+ };
+
+ public interface Callback {
+ void onFooterExpanded();
+ void onSettingsClicked();
+ void onDoneClicked();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index 878ab712..a7f6175 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -23,7 +23,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
-import android.content.res.Resources;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Handler;
@@ -150,6 +149,7 @@ public class ZenModePanel extends LinearLayout {
if (mEmbedded == embedded) return;
mEmbedded = embedded;
mZenButtonsContainer.setLayoutTransition(mEmbedded ? null : newLayoutTransition(null));
+ setLayoutTransition(mEmbedded ? null : newLayoutTransition(null));
if (mEmbedded) {
mZenButtonsContainer.setBackground(null);
} else {
@@ -166,12 +166,10 @@ public class ZenModePanel extends LinearLayout {
super.onFinishInflate();
mZenButtons = (SegmentedButtons) findViewById(R.id.zen_buttons);
- mZenButtons.addButton(R.string.interruption_level_none, R.drawable.ic_zen_none,
- Global.ZEN_MODE_NO_INTERRUPTIONS);
- mZenButtons.addButton(R.string.interruption_level_priority, R.drawable.ic_zen_important,
+ mZenButtons.addButton(R.string.interruption_level_none, Global.ZEN_MODE_NO_INTERRUPTIONS);
+ mZenButtons.addButton(R.string.interruption_level_priority,
Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
- mZenButtons.addButton(R.string.interruption_level_all, R.drawable.ic_zen_all,
- Global.ZEN_MODE_OFF);
+ mZenButtons.addButton(R.string.interruption_level_all, Global.ZEN_MODE_OFF);
mZenButtons.setCallback(mZenButtonsCallback);
mZenButtonsContainer = (ViewGroup) findViewById(R.id.zen_buttons_container);
@@ -275,6 +273,7 @@ public class ZenModePanel extends LinearLayout {
private void setExpanded(boolean expanded) {
if (expanded == mExpanded) return;
+ if (DEBUG) Log.d(mTag, "setExpanded " + expanded);
mExpanded = expanded;
if (mExpanded) {
ensureSelection();
@@ -358,6 +357,10 @@ public class ZenModePanel extends LinearLayout {
return condition == null ? null : condition.copy();
}
+ public String getExitConditionText() {
+ return mExitConditionText;
+ }
+
private void refreshExitConditionText() {
if (mExitCondition == null) {
mExitConditionText = foreverSummary();
@@ -428,7 +431,7 @@ public class ZenModePanel extends LinearLayout {
mZenSubheadExpanded.setVisibility(expanded ? VISIBLE : GONE);
mZenSubheadCollapsed.setVisibility(!expanded ? VISIBLE : GONE);
mMoreSettings.setVisibility(zenImportant && expanded ? VISIBLE : GONE);
- mZenConditions.setVisibility(!zenOff && expanded ? VISIBLE : GONE);
+ mZenConditions.setVisibility(mEmbedded || !zenOff && expanded ? VISIBLE : GONE);
if (zenNone) {
mZenSubheadExpanded.setText(R.string.zen_no_interruptions_with_warning);