summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormezzlasha <mezzlasha@gmail.com>2021-06-01 14:10:43 +0000
committerSemavi Ulusoy <doc.divxm@gmail.com>2021-09-08 09:36:21 +0300
commiteb440511c7d7c37cb1de2a41e842615a18781fb9 (patch)
tree82eae351f8f02c75ced9648e0010f44e4db50d12
parentdc291ae461e351374a24d212f6821fe66ff5ab5a (diff)
plugins: Add ShapeShift Volume Panel Style [3/3]
Signed-off-by: Stallix <joey@evolution-x.org> Change-Id: I67446176a463517141dd600ffe7116757b100411
-rw-r--r--ShapeShiftPanel/Android.bp18
-rw-r--r--ShapeShiftPanel/AndroidManifest.xml42
-rw-r--r--ShapeShiftPanel/proguard.flags2
-rw-r--r--ShapeShiftPanel/res/color/volume_dialog_miui_seekbar_line.xml6
-rw-r--r--ShapeShiftPanel/res/drawable/ic_bluetooth_audio.xml22
-rw-r--r--ShapeShiftPanel/res/drawable/ripple_drawable_20dp_transparent.xml15
-rw-r--r--ShapeShiftPanel/res/drawable/volume_dialog_background.xml23
-rw-r--r--ShapeShiftPanel/res/drawable/volume_dialog_background_line.xml24
-rw-r--r--ShapeShiftPanel/res/drawable/volume_dialog_ringer_bcg.xml23
-rw-r--r--ShapeShiftPanel/res/drawable/volume_dialog_seekbar.xml46
-rw-r--r--ShapeShiftPanel/res/layout/volume_dialog_ssos.xml190
-rw-r--r--ShapeShiftPanel/res/layout/volume_dialog_ssos_row.xml83
-rw-r--r--ShapeShiftPanel/res/layout/volume_dnd_icon_miui.xml32
-rw-r--r--ShapeShiftPanel/res/values/dimens.xml46
-rw-r--r--ShapeShiftPanel/res/values/strings.xml16
-rw-r--r--ShapeShiftPanel/src/co/potatoproject/plugin/volume/ssos/VolumeDialogImpl.java1641
-rw-r--r--VolumePluginCommon/Android.bp1
-rw-r--r--VolumePluginCommon/res/color/volume_dialog_miui_seekbar_line.xml6
-rw-r--r--product_packages_volume_panels.mk1
19 files changed, 2237 insertions, 0 deletions
diff --git a/ShapeShiftPanel/Android.bp b/ShapeShiftPanel/Android.bp
new file mode 100644
index 0000000..8b95af8
--- /dev/null
+++ b/ShapeShiftPanel/Android.bp
@@ -0,0 +1,18 @@
+android_app {
+
+ name: "ShapeShiftPanel",
+
+ static_libs: ["VolumePluginCommon"],
+
+ resource_dirs: ["res"],
+
+ certificate: "platform",
+ optimize: {
+ enabled: false,
+ proguard_flags_files: ["proguard.flags"],
+ },
+ platform_apis: true,
+
+ srcs: ["src/**/*.java"],
+
+}
diff --git a/ShapeShiftPanel/AndroidManifest.xml b/ShapeShiftPanel/AndroidManifest.xml
new file mode 100644
index 0000000..ec161f9
--- /dev/null
+++ b/ShapeShiftPanel/AndroidManifest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+ Copyright (C) 2020 The Potato Open Sauce 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="co.potatoproject.plugin.volume.ssos">
+
+ <uses-permission android:name="com.android.systemui.permission.PLUGIN" />
+
+ <application
+ android:label="@string/plugin_label">
+ <service android:name=".VolumeDialogImpl"
+ android:label="@string/plugin_label">
+ <intent-filter>
+ <action android:name="com.android.systemui.action.PLUGIN_VOLUME" />
+ </intent-filter>
+ </service>
+
+ <provider
+ android:name="androidx.lifecycle.ProcessLifecycleOwnerInitializer"
+ android:authorities="co.potatoproject.plugin.volume.ssos"
+ tools:replace="android:authorities"
+ android:exported="false"
+ android:multiprocess="true" />
+ </application>
+
+</manifest>
diff --git a/ShapeShiftPanel/proguard.flags b/ShapeShiftPanel/proguard.flags
new file mode 100644
index 0000000..4f6fe1f
--- /dev/null
+++ b/ShapeShiftPanel/proguard.flags
@@ -0,0 +1,2 @@
+-keep @com.android.systemui.plugins.Requires public class *
+-keep class javax.inject.** { *; } \ No newline at end of file
diff --git a/ShapeShiftPanel/res/color/volume_dialog_miui_seekbar_line.xml b/ShapeShiftPanel/res/color/volume_dialog_miui_seekbar_line.xml
new file mode 100644
index 0000000..aee14de
--- /dev/null
+++ b/ShapeShiftPanel/res/color/volume_dialog_miui_seekbar_line.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:alpha="0.25"
+ android:color="?android:attr/colorAccent"/>
+</selector>
diff --git a/ShapeShiftPanel/res/drawable/ic_bluetooth_audio.xml b/ShapeShiftPanel/res/drawable/ic_bluetooth_audio.xml
new file mode 100644
index 0000000..3a2ac01
--- /dev/null
+++ b/ShapeShiftPanel/res/drawable/ic_bluetooth_audio.xml
@@ -0,0 +1,22 @@
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+ Copyright (C) 2020 The Potato Open Sauce 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="20.0dp"
+ android:width="20.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12.88,16.29L11,18.17V14.41M11,5.83L12.88,7.71L11,9.58M15.71,7.71L10,2H9V9.58L4.41,5L3,6.41L8.59,12L3,17.58L4.41,19L9,14.41V22H10L15.71,16.29L11.41,12M19.53,6.71L18.26,8C18.89,9.18 19.25,10.55 19.25,12C19.25,13.45 18.89,14.82 18.26,16L19.46,17.22C20.43,15.68 21,13.87 21,11.91C21,10 20.46,8.23 19.53,6.71M14.24,12L16.56,14.33C16.84,13.6 17,12.82 17,12C17,11.18 16.84,10.4 16.57,9.68L14.24,12Z" />
+</vector>
diff --git a/ShapeShiftPanel/res/drawable/ripple_drawable_20dp_transparent.xml b/ShapeShiftPanel/res/drawable/ripple_drawable_20dp_transparent.xml
new file mode 100644
index 0000000..d2fb02c
--- /dev/null
+++ b/ShapeShiftPanel/res/drawable/ripple_drawable_20dp_transparent.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:radius="20dp"
+ android:color="@android:color/transparent" />
diff --git a/ShapeShiftPanel/res/drawable/volume_dialog_background.xml b/ShapeShiftPanel/res/drawable/volume_dialog_background.xml
new file mode 100644
index 0000000..d5bb3d0
--- /dev/null
+++ b/ShapeShiftPanel/res/drawable/volume_dialog_background.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2019 ExtendedUI
+ 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"
+ android:shape="rectangle">
+ <solid android:color="?android:attr/colorBackgroundFloating" />
+ <corners
+ android:bottomLeftRadius="24dp"
+ android:topLeftRadius="24dp"
+ android:bottomRightRadius="24dp"
+ android:topRightRadius="24dp"
+ />
+</shape>
diff --git a/ShapeShiftPanel/res/drawable/volume_dialog_background_line.xml b/ShapeShiftPanel/res/drawable/volume_dialog_background_line.xml
new file mode 100644
index 0000000..65dff2f
--- /dev/null
+++ b/ShapeShiftPanel/res/drawable/volume_dialog_background_line.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2019 ExtendedUI
+ 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"
+ android:shape="rectangle">
+ <solid android:color="@color/volume_dialog_miui_seekbar_line" />
+ <corners
+ android:bottomLeftRadius="24dp"
+ android:topLeftRadius="24dp"
+ android:bottomRightRadius="24dp"
+ android:topRightRadius="24dp"
+ />
+</shape>
+
diff --git a/ShapeShiftPanel/res/drawable/volume_dialog_ringer_bcg.xml b/ShapeShiftPanel/res/drawable/volume_dialog_ringer_bcg.xml
new file mode 100644
index 0000000..978dc06
--- /dev/null
+++ b/ShapeShiftPanel/res/drawable/volume_dialog_ringer_bcg.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2019 ExtendedUI
+ 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"
+ android:shape="rectangle">
+ <solid android:color="@color/accent_tint_color_selector" />
+ <corners
+ android:bottomLeftRadius="40dp"
+ android:topLeftRadius="40dp"
+ android:bottomRightRadius="40dp"
+ android:topRightRadius="40dp"
+ />
+</shape>
diff --git a/ShapeShiftPanel/res/drawable/volume_dialog_seekbar.xml b/ShapeShiftPanel/res/drawable/volume_dialog_seekbar.xml
new file mode 100644
index 0000000..ca65b86
--- /dev/null
+++ b/ShapeShiftPanel/res/drawable/volume_dialog_seekbar.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2019 ExtendedUI
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@android:id/background">
+ <shape android:shape="rectangle">
+ <corners android:radius="30dp"/>
+ <size android:height="42dp" />
+ <solid android:color="?android:attr/colorBackgroundFloating" />
+ </shape>
+ </item>
+ <item android:id="@android:id/progress">
+ <scale
+ android:scaleWidth="100%"
+ android:clipOrientation="horizontal" android:gravity="left">
+ <selector>
+ <item android:state_enabled="false"
+ android:drawable="@android:color/transparent" />
+ <item>
+ <shape android:shape="rectangle"
+ android:tint="?android:attr/colorAccent">
+ <corners
+ android:radius="50dp"/>
+ <size android:height="42dp" />
+ <solid android:color="?android:attr/colorAccent" />
+ </shape>
+ </item>
+ </selector>
+ </scale>
+ </item>
+</layer-list>
diff --git a/ShapeShiftPanel/res/layout/volume_dialog_ssos.xml b/ShapeShiftPanel/res/layout/volume_dialog_ssos.xml
new file mode 100644
index 0000000..861beaf
--- /dev/null
+++ b/ShapeShiftPanel/res/layout/volume_dialog_ssos.xml
@@ -0,0 +1,190 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+ Copyright (C) 2020 The Potato Open Sauce Project
+ Copyright ExtendedUI
+ Copyright SynthOS
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:sysui="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/volume_dialog_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="right"
+ android:layout_gravity="right"
+ android:background="@android:color/transparent"
+ android:theme="@style/qs_theme">
+
+ <!-- right-aligned to be physically near volume button -->
+ <LinearLayout
+ android:id="@+id/volume_dialog"
+ android:minWidth="@dimen/volume_dialog_miui_panel_width"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@android:color/transparent"
+ android:paddingRight="@dimen/volume_dialog_miui_panel_transparent_padding_right"
+ android:paddingTop="@dimen/volume_dialog_panel_transparent_padding"
+ android:paddingBottom="@dimen/volume_dialog_panel_transparent_padding"
+ android:paddingLeft="@dimen/volume_dialog_miui_panel_transparent_padding_right"
+ android:orientation="vertical"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:id="@+id/main"
+ android:minWidth="@dimen/volume_dialog_miui_panel_width"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:animateLayoutChanges="true"
+ android:orientation="vertical"
+ android:translationZ="@dimen/volume_dialog_elevation"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:background="@drawable/volume_dialog_background">
+
+ <FrameLayout
+ android:id="@+id/ringer"
+ android:layout_width="42dp"
+ android:layout_height="42dp"
+ android:layout_margin="@dimen/volume_dialog_miui_ringer_spacing"
+ android:background="@drawable/volume_dialog_ringer_bcg">
+
+ <co.potatoproject.plugin.volume.common.AlphaOptimizedImageButton
+ android:id="@+id/ringer_icon"
+ style="@style/VolumeButtons"
+ android:layout_width="35dp"
+ android:layout_height="35dp"
+ android:scaleType="fitCenter"
+ android:padding="@dimen/volume_dialog_miui_ringer_icon_padding"
+ android:background="@drawable/ripple_drawable_20dp_transparent"
+ android:tint="@android:color/white"
+ android:layout_gravity="center"
+ android:soundEffectsEnabled="false" />
+
+ <include layout="@layout/volume_dnd_icon_miui"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="@dimen/volume_dialog_stream_padding"
+ android:layout_marginTop="6dp"/>
+ </FrameLayout>
+
+ <LinearLayout
+ android:id="@+id/volume_dialog_rows"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginTop="6dp"
+ android:layout_marginBottom="2dp"
+ android:layout_marginHorizontal="2dp"
+ android:minWidth="@dimen/volume_dialog_miui_panel_width"
+ android:gravity="center"
+ android:orientation="horizontal"
+ android:animateLayoutChanges="true">
+ <!-- volume rows added and removed here! :-) -->
+
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/main_buttons"
+ android:layout_width="50dp"
+ android:layout_height="50dp"
+ android:layout_marginTop="@dimen/volume_dialog_miui_spacer"
+ android:gravity="center"
+ android:layout_gravity="center"
+ android:clipToPadding="false"
+ android:translationZ="@dimen/volume_dialog_elevation"
+ android:background="@drawable/volume_dialog_background"
+ android:orientation="vertical">
+
+
+
+ <LinearLayout
+ android:id="@+id/expandable_indicator_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/volume_dialog_ringer_bcg"
+ android:layout_margin="4dp"
+ android:gravity="center"
+ android:layout_gravity="center"
+ android:animateLayoutChanges="true">
+ <co.potatoproject.plugin.volume.common.AlphaOptimizedImageButton
+ android:id="@+id/media_output"
+ android:src="@drawable/ic_bluetooth_audio"
+ android:layout_width="@dimen/volume_dialog_miui_tap_target_size"
+ android:layout_height="@dimen/volume_dialog_miui_tap_target_size"
+ android:visibility="gone"
+ android:layout_marginLeft="8dp"
+ android:layout_marginRight="8dp"
+ android:layout_gravity="center"
+ android:contentDescription="@string/accessibility_volume_settings"
+ android:background="@drawable/ripple_drawable_20dp_transparent"
+ android:tint="?android:attr/textColorSecondary"
+ android:animateLayoutChanges="true"
+ android:soundEffectsEnabled="false" />
+
+ <View
+ android:id="@+id/dummy_button"
+ android:layout_height="1dp"
+ android:layout_width="1dp" />
+
+ <co.potatoproject.plugin.volume.common.ExpandableIndicator
+ android:id="@+id/expandable_indicator"
+ android:layout_width="@dimen/volume_dialog_miui_tap_target_size"
+ android:layout_height="@dimen/volume_dialog_miui_tap_target_size"
+ android:clipToPadding="false"
+ android:clickable="true"
+ android:focusable="true"
+ android:layout_marginLeft="8dp"
+ android:layout_marginRight="8dp"
+ android:layout_gravity="center"
+ android:contentDescription="@string/accessibility_quick_settings_expand"
+ android:background="@drawable/ripple_drawable_20dp_transparent"
+ android:tint="#FFFFFF"
+ android:soundEffectsEnabled="false"
+ android:padding="16dp"
+ android:rotation="90" />
+ </LinearLayout>
+ </LinearLayout>
+
+ <FrameLayout
+ android:id="@+id/odi_captions"
+ android:layout_width="50dp"
+ android:layout_height="50dp"
+ android:layout_marginTop="@dimen/volume_dialog_miui_spacer"
+ android:gravity="right"
+ android:layout_gravity="right"
+ android:clipToPadding="false"
+ android:translationZ="@dimen/volume_dialog_elevation"
+ android:background="@drawable/volume_dialog_background">
+ <co.potatoproject.plugin.volume.common.CaptionsToggleImageButton
+ android:id="@+id/odi_captions_icon"
+ android:layout_margin="8dp"
+ android:background="@drawable/ripple_drawable_20dp"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:tint="?android:attr/textColorSecondary"
+ android:layout_gravity="center"
+ android:soundEffectsEnabled="false"
+ sysui:optedOut="false"/>
+ </FrameLayout>
+ </LinearLayout>
+
+ <ViewStub
+ android:id="@+id/odi_captions_tooltip_stub"
+ android:inflatedId="@+id/odi_captions_tooltip_view"
+ android:layout="@layout/volume_tool_tip_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="@dimen/volume_tool_tip_right_margin"
+ android:layout_marginBottom="@dimen/volume_tool_tip_bottom_margin"/>
+
+</FrameLayout>
diff --git a/ShapeShiftPanel/res/layout/volume_dialog_ssos_row.xml b/ShapeShiftPanel/res/layout/volume_dialog_ssos_row.xml
new file mode 100644
index 0000000..bb34576
--- /dev/null
+++ b/ShapeShiftPanel/res/layout/volume_dialog_ssos_row.xml
@@ -0,0 +1,83 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/volume_dialog_miui_panel_width"
+ android:layout_height="wrap_content"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:tag="row"
+ android:theme="@style/qs_theme">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/volume_row_header"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLength="10"
+ android:maxLines="1"
+ android:textAppearance="@style/TextAppearance.Volume.Header"
+ android:textColor="?android:attr/colorControlNormal"
+ android:visibility="gone" />
+
+ <androidx.cardview.widget.CardView
+ android:layout_width="match_parent"
+ app:cardCornerRadius="50dp"
+ app:cardBackgroundColor="?android:attr/colorBackgroundFloating"
+ app:cardElevation="0dp"
+ app:contentPadding="-0dp"
+ android:layout_height="match_parent">
+
+ <FrameLayout
+ android:id="@+id/volume_row_slider_frame"
+ android:layout_width="match_parent"
+ android:layout_height="192dp"
+ android:layoutDirection="rtl">
+
+ <SeekBar
+ android:id="@+id/volume_row_slider"
+ android:layout_width="@dimen/custom_volume_dialog_miui_seekbar_height"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:background="@null"
+ android:clickable="true"
+ android:layoutDirection="rtl"
+ android:paddingTop="0dp"
+ android:paddingBottom="0dp"
+ android:progress="10"
+ android:progressDrawable="@drawable/volume_dialog_seekbar"
+ android:rotation="-90"
+ android:thumb="@android:color/transparent" />
+
+ <ImageView
+ android:layout_width="4dp"
+ android:layout_gravity="center"
+ android:layout_height="match_parent"
+ android:layout_marginTop="33dp"
+ android:layout_marginBottom="48dp"
+ android:background="@drawable/volume_dialog_background_line"/>
+
+ </FrameLayout>
+ </androidx.cardview.widget.CardView>
+
+
+ <co.potatoproject.plugin.volume.common.AlphaOptimizedImageButton
+ android:id="@+id/volume_row_icon"
+ style="@style/VolumeButtons"
+ android:layout_width="@dimen/volume_dialog_miui_tap_target_size"
+ android:layout_height="@dimen/volume_dialog_miui_tap_target_size"
+ android:layout_marginTop="-51dp"
+ android:background="@drawable/ripple_drawable_20dp"
+ android:clickable="false"
+ android:soundEffectsEnabled="false"
+ android:tint="@android:color/white" />
+ </LinearLayout>
+
+ <include layout="@layout/volume_dnd_icon_miui" />
+
+</FrameLayout>
diff --git a/ShapeShiftPanel/res/layout/volume_dnd_icon_miui.xml b/ShapeShiftPanel/res/layout/volume_dnd_icon_miui.xml
new file mode 100644
index 0000000..49cd452
--- /dev/null
+++ b/ShapeShiftPanel/res/layout/volume_dnd_icon_miui.xml
@@ -0,0 +1,32 @@
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+ Copyright (C) 2020 The Potato Open Sauce Project
+ Copyright 2020 ExtendedUI
+
+ 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:id="@+id/dnd_icon"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="6dp">
+
+ <ImageView
+ android:layout_width="14dp"
+ android:layout_height="14dp"
+ android:visibility="gone"
+ android:layout_gravity="right|top"
+ android:src="@*android:drawable/ic_qs_dnd"
+ android:tint="@android:color/white"/>
+</FrameLayout>
diff --git a/ShapeShiftPanel/res/values/dimens.xml b/ShapeShiftPanel/res/values/dimens.xml
new file mode 100644
index 0000000..68c2bca
--- /dev/null
+++ b/ShapeShiftPanel/res/values/dimens.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Potato Open Sauce Project
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <dimen name="borderless_button_radius">2dp</dimen>
+ <dimen name="volume_icon_size">20dp</dimen>
+ <dimen name="volume_button_size">48dp</dimen>
+ <dimen name="volume_dialog_caption_size">64dp</dimen>
+ <dimen name="volume_dialog_elevation">9dp</dimen>
+ <dimen name="volume_dialog_margin_bottom">4dp</dimen>
+ <dimen name="volume_dialog_padding_top">8dp</dimen>
+ <dimen name="volume_dialog_panel_transparent_padding">20dp</dimen>
+ <dimen name="volume_dialog_panel_transparent_padding_right">4dp</dimen>
+ <dimen name="volume_dialog_panel_width">64dp</dimen>
+ <dimen name="volume_dialog_ringer_icon_padding">20dp</dimen>
+ <dimen name="volume_dialog_ringer_size">64dp</dimen>
+ <dimen name="volume_dialog_row_margin_bottom">8dp</dimen>
+ <dimen name="volume_dialog_slider_height">116dp</dimen>
+ <dimen name="volume_dialog_slider_margin_bottom">-2dp</dimen>
+ <dimen name="volume_dialog_slider_margin_top">14dp</dimen>
+ <dimen name="volume_dialog_spacer">4dp</dimen>
+ <dimen name="volume_dialog_stream_padding">8dp</dimen>
+ <dimen name="volume_dialog_tap_target_size">48dp</dimen>
+ <dimen name="volume_expander_margin_end">2dp</dimen>
+ <dimen name="volume_row_header_padding_start">16dp</dimen>
+ <dimen name="volume_row_height">64dp</dimen>
+ <dimen name="volume_row_padding_bottom">9.4dp</dimen>
+ <dimen name="volume_row_padding_start">4dp</dimen>
+ <dimen name="volume_row_slider_height">48dp</dimen>
+ <dimen name="volume_row_slider_padding_start">12dp</dimen>
+ <dimen name="volume_tool_tip_bottom_margin">32dp</dimen>
+ <dimen name="volume_tool_tip_right_margin">76dp</dimen>
+ <dimen name="volume_row_app_icon_padding">1dp</dimen>
+ <dimen name="volume_dialog_tap_target_app_size">38dip</dimen>
+ <dimen name="volume_dialog_app_row_icon_margintop">-50dp</dimen>
+</resources>
diff --git a/ShapeShiftPanel/res/values/strings.xml b/ShapeShiftPanel/res/values/strings.xml
new file mode 100644
index 0000000..a7a2ba6
--- /dev/null
+++ b/ShapeShiftPanel/res/values/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Potato Open Sauce Project
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <string name="plugin_label">MIUI Compact</string>
+</resources>
diff --git a/ShapeShiftPanel/src/co/potatoproject/plugin/volume/ssos/VolumeDialogImpl.java b/ShapeShiftPanel/src/co/potatoproject/plugin/volume/ssos/VolumeDialogImpl.java
new file mode 100644
index 0000000..05994de
--- /dev/null
+++ b/ShapeShiftPanel/src/co/potatoproject/plugin/volume/ssos/VolumeDialogImpl.java
@@ -0,0 +1,1641 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2020 The Potato Open Sauce 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 co.potatoproject.plugin.volume.ssos;
+
+import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
+import static android.media.AudioManager.RINGER_MODE_NORMAL;
+import static android.media.AudioManager.RINGER_MODE_SILENT;
+import static android.media.AudioManager.RINGER_MODE_VIBRATE;
+import static android.media.AudioManager.STREAM_ACCESSIBILITY;
+import static android.media.AudioManager.STREAM_ALARM;
+import static android.media.AudioManager.STREAM_MUSIC;
+import static android.media.AudioManager.STREAM_RING;
+import static android.media.AudioManager.STREAM_VOICE_CALL;
+import static android.view.View.ACCESSIBILITY_LIVE_REGION_POLITE;
+import static android.view.View.GONE;
+import static android.view.View.INVISIBLE;
+import static android.view.View.VISIBLE;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+import static co.potatoproject.plugin.volume.common.Events.DISMISS_REASON_SETTINGS_CLICKED;
+
+import android.animation.ObjectAnimator;
+import android.annotation.SuppressLint;
+import android.app.ActivityManager;
+import android.app.Dialog;
+import android.app.KeyguardManager;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothProfile;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.ColorStateList;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.PorterDuff;
+import android.media.AudioManager;
+import android.media.AudioSystem;
+import android.media.MediaMetadata;
+import android.media.session.MediaController;
+import android.media.session.PlaybackState;
+import android.os.Debug;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.os.VibrationEffect;
+import android.provider.Settings;
+import android.provider.Settings.Global;
+import android.text.InputFilter;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseBooleanArray;
+import android.view.ContextThemeWrapper;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.AccessibilityDelegate;
+import android.view.View.OnLayoutChangeListener;
+import android.view.ViewGroup;
+import android.view.ViewPropertyAnimator;
+import android.view.ViewStub;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.InternalInsetsInfo;
+import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.animation.DecelerateInterpolator;
+import android.widget.FrameLayout;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import co.potatoproject.plugin.volume.common.*;
+
+import co.potatoproject.plugin.volume.ssos.R;
+
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.PluginDependency;
+import com.android.systemui.plugins.VolumeDialog;
+import com.android.systemui.plugins.VolumeDialogController.State;
+import com.android.systemui.plugins.VolumeDialogController.StreamState;
+import com.android.systemui.plugins.VolumeDialogController;
+import com.android.systemui.plugins.annotations.Requires;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Visual presentation of the volume dialog.
+ *
+ * A client of VolumeDialogControllerImpl and its state model.
+ *
+ * Methods ending in "H" must be called on the (ui) handler.
+ */
+@Requires(target = VolumeDialog.class, version = VolumeDialog.VERSION)
+@Requires(target = VolumeDialog.Callback.class, version = VolumeDialog.Callback.VERSION)
+@Requires(target = VolumeDialogController.class, version = VolumeDialogController.VERSION)
+@Requires(target = ActivityStarter.class, version = ActivityStarter.VERSION)
+public class VolumeDialogImpl implements VolumeDialog {
+ private static final String TAG = Utils.logTag(VolumeDialogImpl.class);
+ public static final String ACTION_MEDIA_OUTPUT =
+ "com.android.settings.panel.action.MEDIA_OUTPUT";
+
+ private static final long USER_ATTEMPT_GRACE_PERIOD = 1000;
+ private static final int UPDATE_ANIMATION_DURATION = 80;
+
+ static final int DIALOG_TIMEOUT_MILLIS = 3000;
+ static final int DIALOG_SAFETYWARNING_TIMEOUT_MILLIS = 5000;
+ static final int DIALOG_ODI_CAPTIONS_TOOLTIP_TIMEOUT_MILLIS = 5000;
+ static final int DIALOG_HOVERING_TIMEOUT_MILLIS = 16000;
+ static final int DIALOG_SHOW_ANIMATION_DURATION = 300;
+ static final int DIALOG_HIDE_ANIMATION_DURATION = 250;
+
+ private SysUIR mSysUIR;
+ private Context mContext;
+ private Context mSysUIContext;
+ private WindowManager mWindowManager;
+ private WindowManager.LayoutParams mWindowParams;
+ private final H mHandler = new H();
+ private VolumeDialogController mController;
+
+ private View mDialog;
+ private LinearLayout mDialogView;
+ private ViewGroup mDialogRowsView;
+ private ViewGroup mRinger;
+ private ImageButton mRingerIcon;
+ private ViewGroup mODICaptionsView;
+ private CaptionsToggleImageButton mODICaptionsIcon;
+ private View mMediaOutputView;
+ private ImageButton mMediaOutputIcon;
+ private View mExpandRowsView;
+ private ExpandableIndicator mExpandRows;
+ private FrameLayout mZenIcon;
+ private final List<VolumeRow> mRows = new ArrayList<>();
+ private ConfigurableTexts mConfigurableTexts;
+ private SparseBooleanArray mDynamic = new SparseBooleanArray();
+ private KeyguardManager mKeyguard;
+ private ActivityManager mActivityManager;
+ private AccessibilityManager mAccessibilityMgr;
+ private final Object mSafetyWarningLock = new Object();
+ private final Accessibility mAccessibility = new Accessibility();
+
+ private boolean mShowing;
+ private boolean mShowA11yStream;
+
+ private int mActiveStream;
+ private int mPrevActiveStream;
+ private boolean mAutomute = VolumePrefs.DEFAULT_ENABLE_AUTOMUTE;
+ private boolean mSilentMode = VolumePrefs.DEFAULT_ENABLE_SILENT_MODE;
+ private State mState;
+ private SafetyWarningDialog mSafetyWarning;
+ private boolean mHovering = false;
+ private boolean mShowActiveStreamOnly;
+ private boolean mConfigChanged = false;
+ private boolean mHasSeenODICaptionsTooltip;
+ private ViewStub mODICaptionsTooltipViewStub;
+ private View mODICaptionsTooltipView = null;
+
+ private boolean mExpanded;
+
+ private boolean mLeftVolumeRocker;
+
+ private View[] views;
+ private Drawable[] defaultDrawables;
+ private String[] defaultDrawablesNames;
+
+ public VolumeDialogImpl() {}
+
+ @Override
+ public void onCreate(Context sysuiContext, Context pluginContext) {
+ mSysUIR = new SysUIR(pluginContext);
+ mContext = pluginContext;
+ mSysUIContext =
+ new ContextThemeWrapper(sysuiContext, mSysUIR.style("qs_theme", sysuiContext));
+ mController = PluginDependency.get(this, VolumeDialogController.class);
+ mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
+ mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
+ mAccessibilityMgr = mContext.getSystemService(AccessibilityManager.class);
+ mShowActiveStreamOnly = showActiveStreamOnly();
+ mHasSeenODICaptionsTooltip =
+ Prefs.getBoolean(sysuiContext, Prefs.Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP, false);
+ mLeftVolumeRocker = Settings.System.getInt(mContext.getContentResolver(), Settings.System.VOLUME_PANEL_ON_LEFT, 0) == 1;
+ }
+
+ public void init(int windowType, Callback callback) {
+ initDialog();
+
+ mAccessibility.init();
+
+ mController.addCallback(mControllerCallbackH, mHandler);
+ mController.getState();
+ }
+
+ @Override
+ public void destroy() {
+ mController.removeCallback(mControllerCallbackH);
+ mHandler.removeCallbacksAndMessages(null);
+ }
+
+ private void initDialog() {
+ mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+
+ mSysUIContext.getTheme().applyStyle(mSysUIContext.getThemeResId(), true);
+ mSysUIContext.getTheme().rebase();
+ mContext.getTheme().setTo(mSysUIContext.getTheme());
+
+ mConfigurableTexts = new ConfigurableTexts(mContext);
+ mHovering = false;
+ mShowing = false;
+ mExpanded = false;
+ mWindowParams = new WindowManager.LayoutParams();
+ mWindowParams.flags &= ~WindowManager.LayoutParams.FLAG_DIM_BEHIND;
+ mWindowParams.flags &= ~WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
+ mWindowParams.flags |= 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;
+ mWindowParams.type = WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
+ mWindowParams.format = PixelFormat.TRANSLUCENT;
+ mWindowParams.windowAnimations = -1;
+ mDialog = LayoutInflater.from(mContext).inflate(R.layout.volume_dialog_ssos,
+ (ViewGroup) null, false);
+
+ mDialog.setOnTouchListener((v, event) -> {
+ if (mShowing) {
+ if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
+ dismissH(Events.DISMISS_REASON_TOUCH_OUTSIDE);
+ return true;
+ }
+ }
+ return false;
+ });
+
+ mDialogView = mDialog.findViewById(R.id.volume_dialog);
+ mDialogView.setAlpha(0);
+
+ mDialogView.setOnHoverListener((v, event) -> {
+ int action = event.getActionMasked();
+ mHovering = (action == MotionEvent.ACTION_HOVER_ENTER)
+ || (action == MotionEvent.ACTION_HOVER_MOVE);
+ rescheduleTimeoutH();
+ return true;
+ });
+
+ FrameLayout.LayoutParams dialogViewLP =
+ (FrameLayout.LayoutParams) mDialogView.getLayoutParams();
+ dialogViewLP.gravity = Gravity.CENTER_VERTICAL;
+ mDialogView.setLayoutParams(dialogViewLP);
+
+ mDialogRowsView = mDialog.findViewById(R.id.volume_dialog_rows);
+ mRinger = mDialog.findViewById(R.id.ringer);
+ if (mRinger != null) {
+ mRingerIcon = mRinger.findViewById(R.id.ringer_icon);
+ mZenIcon = mRinger.findViewById(R.id.dnd_icon);
+ }
+
+ mODICaptionsView = mDialog.findViewById(R.id.odi_captions);
+ if (mODICaptionsView != null) {
+ mODICaptionsIcon = mODICaptionsView.findViewById(R.id.odi_captions_icon);
+ mODICaptionsIcon.setImageDrawable(
+ mSysUIContext.getDrawable(mSysUIR.drawable("ic_volume_odi_captions_disabled")));
+ }
+ mODICaptionsTooltipViewStub = mDialog.findViewById(R.id.odi_captions_tooltip_stub);
+ if (mHasSeenODICaptionsTooltip && mODICaptionsTooltipViewStub != null) {
+ mDialogView.removeView(mODICaptionsTooltipViewStub);
+ mODICaptionsTooltipViewStub = null;
+ }
+
+ mMediaOutputIcon = mDialog.findViewById(R.id.media_output);
+
+ mExpandRowsView = mDialog.findViewById(R.id.expandable_indicator_container);
+ mExpandRows = mDialog.findViewById(R.id.expandable_indicator);
+
+ LinearLayout.LayoutParams mainLP =
+ (LinearLayout.LayoutParams) mDialog.findViewById(R.id.main).getLayoutParams();
+ LinearLayout.LayoutParams captionsLP =
+ (LinearLayout.LayoutParams) mODICaptionsView.getLayoutParams();
+ LinearLayout.LayoutParams buttonsLP =
+ (LinearLayout.LayoutParams) mDialog.findViewById(R.id.main_buttons).getLayoutParams();
+
+ if(!isAudioPanelOnLeftSide()) {
+ mainLP.gravity = Gravity.RIGHT;
+ mExpandRows.setRotation(90);
+ captionsLP.gravity = Gravity.RIGHT;
+ buttonsLP.gravity = Gravity.RIGHT;
+ } else {
+ mainLP.gravity = Gravity.LEFT;
+ mExpandRows.setRotation(-90);
+ captionsLP.gravity = Gravity.LEFT;
+ buttonsLP.gravity = Gravity.LEFT;
+ }
+
+ mDialog.findViewById(R.id.main).setLayoutParams(mainLP);
+ mODICaptionsView.setLayoutParams(captionsLP);
+ mDialog.findViewById(R.id.main_buttons).setLayoutParams(buttonsLP);
+
+ if (mRows.isEmpty()) {
+ if (!AudioSystem.isSingleVolume(mContext)) {
+ addRow(STREAM_ACCESSIBILITY, mSysUIR.drawable("ic_volume_accessibility"),
+ mSysUIR.drawable("ic_volume_accessibility"), true, false);
+ }
+ addRow(AudioManager.STREAM_MUSIC,
+ mSysUIR.drawable("ic_volume_media"), mSysUIR.drawable("ic_volume_media_mute"), true, true);
+ if (!AudioSystem.isSingleVolume(mContext)) {
+ addRow(AudioManager.STREAM_RING,
+ mSysUIR.drawable("ic_volume_ringer"), mSysUIR.drawable("ic_volume_ringer_mute"), true, false);
+ addRow(STREAM_ALARM,
+ mSysUIR.drawable("ic_alarm"), mSysUIR.drawable("ic_volume_alarm_mute"), true, false);
+ addRow(AudioManager.STREAM_VOICE_CALL,
+ com.android.internal.R.drawable.ic_phone,
+ com.android.internal.R.drawable.ic_phone, false, false);
+ addRow(AudioManager.STREAM_BLUETOOTH_SCO,
+ mSysUIR.drawable("ic_volume_bt_sco"), mSysUIR.drawable("ic_volume_bt_sco"), false, false);
+ addRow(AudioManager.STREAM_SYSTEM, mSysUIR.drawable("ic_volume_system"),
+ mSysUIR.drawable("ic_volume_system_mute"), false, false);
+ }
+ } else {
+ addExistingRows();
+ }
+
+ updateRowsH(getActiveRow());
+ initRingerH();
+ initSettingsH();
+ initODICaptionsH();
+ }
+
+ private final OnComputeInternalInsetsListener mInsetsListener = internalInsetsInfo -> {
+ internalInsetsInfo.touchableRegion.setEmpty();
+ internalInsetsInfo.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+ View main = mDialog.findViewById(R.id.main);
+ int[] mainLocation = new int[2];
+ main.getLocationInWindow(mainLocation);
+ int[] dialogLocation = new int[2];
+ mDialogView.getLocationInWindow(dialogLocation);
+ internalInsetsInfo.touchableRegion.set(new Region(
+ mainLocation[0] - main.getWidth(),
+ dialogLocation[1],
+ mainLocation[0] + main.getWidth(),
+ dialogLocation[1] + mDialogView.getHeight()
+ ));
+ };
+
+ protected ViewGroup getDialogView() {
+ return mDialogView;
+ }
+
+ private int getAlphaAttr(int attr) {
+ TypedArray ta = mContext.obtainStyledAttributes(new int[]{attr});
+ float alpha = ta.getFloat(0, 0);
+ ta.recycle();
+ return (int) (alpha * 255);
+ }
+
+ private boolean isLandscape() {
+ return mContext.getResources().getConfiguration().orientation ==
+ Configuration.ORIENTATION_LANDSCAPE;
+ }
+
+ public void setStreamImportant(int stream, boolean important) {
+ mHandler.obtainMessage(H.SET_STREAM_IMPORTANT, stream, important ? 1 : 0).sendToTarget();
+ }
+
+ 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,
+ boolean defaultStream) {
+ addRow(stream, iconRes, iconMuteRes, important, defaultStream, false);
+ }
+
+ private void addRow(int stream, int iconRes, int iconMuteRes, boolean important,
+ boolean defaultStream, boolean dynamic) {
+ if (D.BUG) Slog.d(TAG, "Adding row for stream " + stream);
+ VolumeRow row = new VolumeRow();
+ initRow(row, stream, iconRes, iconMuteRes, important, defaultStream);
+ if(!isAudioPanelOnLeftSide()){
+ mDialogRowsView.addView(row.view);
+ } else {
+ mDialogRowsView.addView(row.view);
+ }
+ mRows.add(row);
+ }
+
+ private void addExistingRows() {
+ int N = mRows.size();
+ for (int i = 0; i < N; i++) {
+ final VolumeRow row = mRows.get(i);
+ initRow(row, row.stream, row.iconRes, row.iconMuteRes, row.important,
+ row.defaultStream);
+ if(!isAudioPanelOnLeftSide()){
+ mDialogRowsView.addView(row.view, 0);
+ } else {
+ mDialogRowsView.addView(row.view);
+ }
+ updateVolumeRowH(row);
+ }
+ }
+
+ private void cleanExpandedRows() {
+ for (int i = mRows.size() - 1; i >= 0; i--) {
+ final VolumeRow row = mRows.get(i);
+ if (row.stream == AudioManager.STREAM_RING || row.stream == AudioManager.STREAM_NOTIFICATION) {
+ removeRow(row);
+ }
+ }
+ }
+
+ private void removeRow(VolumeRow volumeRow) {
+ mRows.remove(volumeRow);
+ mDialogRowsView.removeView(volumeRow.view);
+ }
+
+ private void updateAllActiveRows() {
+ int N = mRows.size();
+ for (int i = 0; i < N; i++) {
+ updateVolumeRowH(mRows.get(i));
+ }
+ }
+
+ private VolumeRow getActiveRow() {
+ for (VolumeRow row : mRows) {
+ if (row.stream == mActiveStream) {
+ return row;
+ }
+ }
+ for (VolumeRow row : mRows) {
+ if (row.stream == STREAM_MUSIC) {
+ return row;
+ }
+ }
+ return mRows.get(0);
+ }
+
+ private VolumeRow findRow(int stream) {
+ for (VolumeRow row : mRows) {
+ if (row.stream == stream) return row;
+ }
+ return null;
+ }
+
+ public void dump(PrintWriter writer) {
+ writer.println(VolumeDialogImpl.class.getSimpleName() + " state:");
+ writer.print(" mShowing: "); writer.println(mShowing);
+ writer.print(" mActiveStream: "); writer.println(mActiveStream);
+ writer.print(" mDynamic: "); writer.println(mDynamic);
+ 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 void initRow(final VolumeRow row, final int stream, int iconRes, int iconMuteRes,
+ boolean important, boolean defaultStream) {
+ row.stream = stream;
+ row.iconRes = iconRes;
+ row.iconMuteRes = iconMuteRes;
+ row.important = important;
+ row.defaultStream = defaultStream;
+ row.view = LayoutInflater.from(mContext).inflate(R.layout.volume_dialog_ssos_row, null);
+ row.view.setId(row.stream);
+ row.view.setTag(row);
+ row.header = row.view.findViewById(R.id.volume_row_header);
+ row.header.setId(20 * row.stream);
+ if (stream == STREAM_ACCESSIBILITY) {
+ row.header.setFilters(new InputFilter[] {new InputFilter.LengthFilter(13)});
+ }
+ row.dndIcon = row.view.findViewById(R.id.dnd_icon);
+ row.slider = row.view.findViewById(R.id.volume_row_slider);
+ row.slider.setProgressTintMode(PorterDuff.Mode.SRC_ATOP);
+ row.slider.setThumbTintMode(PorterDuff.Mode.SRC_ATOP);
+ row.slider.setOnSeekBarChangeListener(new VolumeSeekBarChangeListener(row));
+
+ row.anim = null;
+
+ row.icon = row.view.findViewById(R.id.volume_row_icon);
+ Drawable iconResDrawable = mSysUIContext.getDrawable(iconRes);
+ row.icon.setImageDrawable(iconResDrawable);
+ row.icon.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+ row.icon.setClickable(false);
+ ColorStateList mIconTint = ColorStateList.valueOf(mContext.getResources().getColor(R.color.row_icon_dark));
+ ColorStateList mIconTintNormal = ColorStateList.valueOf(mContext.getResources().getColor(R.color.row_icon_white));
+ int rowprogress = row.slider.getProgress();
+ if (rowprogress < 500) {
+ row.icon.setImageTintList(mIconTint);
+ } else {
+ row.icon.setImageTintList(mIconTintNormal);
+ }
+ }
+
+ private static boolean isBluetoothA2dpConnected() {
+ BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ return mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()
+ && mBluetoothAdapter.getProfileConnectionState(BluetoothProfile.A2DP)
+ == BluetoothProfile.STATE_CONNECTED;
+ }
+
+ private void updateOutputSwitcherVisibility() {
+ View dummyButton = mDialog.findViewById(R.id.dummy_button);
+
+ if(isBluetoothA2dpConnected()) {
+ mMediaOutputIcon.setVisibility(
+ mActivityManager.getLockTaskModeState() == LOCK_TASK_MODE_NONE &&
+ mExpanded ? VISIBLE : GONE);
+ dummyButton.setVisibility(GONE);
+ } else {
+ dummyButton.setVisibility(
+ mActivityManager.getLockTaskModeState() == LOCK_TASK_MODE_NONE &&
+ mExpanded ? VISIBLE : GONE);
+ mMediaOutputIcon.setVisibility(GONE);
+ }
+ }
+
+ private boolean shouldShowNotificationStream() {
+ ContentResolver ns = mContext.getContentResolver();
+ return Settings.Secure.getInt(ns, Settings.Secure.VOLUME_LINK_NOTIFICATION, 1) == 1;
+ }
+
+ public void initSettingsH() {
+ if (mMediaOutputIcon != null) {
+ mMediaOutputIcon.setOnClickListener(v -> {
+ Events.writeEvent(Events.EVENT_SETTINGS_CLICK);
+ Intent intent = new Intent(ACTION_MEDIA_OUTPUT);
+ dismissH(DISMISS_REASON_SETTINGS_CLICKED);
+ PluginDependency.get(this, ActivityStarter.class).startActivity(intent,
+ true /* dismissShade */);
+ });
+ }
+ if (mExpandRowsView != null) {
+ mExpandRowsView.setVisibility(
+ mActivityManager.getLockTaskModeState() == LOCK_TASK_MODE_NONE ?
+ VISIBLE : GONE);
+ updateOutputSwitcherVisibility();
+ }
+ if (mExpandRows != null) {
+ mExpandRows.setOnLongClickListener(v -> {
+ Events.writeEvent(Events.EVENT_SETTINGS_CLICK);
+ Intent intent = new Intent(Settings.Panel.ACTION_VOLUME);
+ dismissH(DISMISS_REASON_SETTINGS_CLICKED);
+ PluginDependency.get(this, ActivityStarter.class).startActivity(intent,
+ true /* dismissShade */);
+ return true;
+ });
+ mExpandRows.setOnClickListener(v -> {
+ rescheduleTimeoutH();
+ if (!mExpanded) {
+ addRow(AudioManager.STREAM_RING, mSysUIR.drawable("ic_volume_ringer"),
+ mSysUIR.drawable("ic_volume_ringer_mute"), true, false);
+ if (!shouldShowNotificationStream()) {
+ addRow(AudioManager.STREAM_NOTIFICATION, mSysUIR.drawable("ic_volume_notification"),
+ mSysUIR.drawable("ic_volume_notification_mute"), true, false);
+ }
+ updateAllActiveRows();
+ mExpanded = true;
+ updateOutputSwitcherVisibility();
+ } else {
+ cleanExpandedRows();
+ mExpanded = false;
+ updateOutputSwitcherVisibility();
+ }
+ mExpandRows.setExpanded(mExpanded);
+ });
+ }
+ }
+
+ public void initRingerH() {
+ if (mRingerIcon != null) {
+ mRingerIcon.setAccessibilityLiveRegion(ACCESSIBILITY_LIVE_REGION_POLITE);
+ mRingerIcon.setOnClickListener(v -> {
+ rescheduleTimeoutH();
+ Prefs.putBoolean(mSysUIContext, Prefs.Key.TOUCHED_RINGER_TOGGLE, true);
+ final StreamState ss = mState.states.get(AudioManager.STREAM_RING);
+ if (ss == null) {
+ return;
+ }
+ // normal -> vibrate -> silent -> normal (skip vibrate if device doesn't have
+ // a vibrator.
+ int newRingerMode;
+ final boolean hasVibrator = mController.hasVibrator();
+ if (mState.ringerModeInternal == AudioManager.RINGER_MODE_NORMAL) {
+ if (hasVibrator) {
+ newRingerMode = AudioManager.RINGER_MODE_VIBRATE;
+ } else {
+ newRingerMode = AudioManager.RINGER_MODE_SILENT;
+ }
+ } else if (mState.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE) {
+ newRingerMode = AudioManager.RINGER_MODE_SILENT;
+ } else {
+ newRingerMode = AudioManager.RINGER_MODE_NORMAL;
+ if (ss.level == 0) {
+ mController.setStreamVolume(AudioManager.STREAM_RING, 1);
+ }
+ }
+ Events.writeEvent(Events.EVENT_RINGER_TOGGLE, newRingerMode);
+ incrementManualToggleCount();
+ updateRingerH();
+ provideTouchFeedbackH(newRingerMode);
+ mController.setRingerMode(newRingerMode, false);
+ maybeShowToastH(newRingerMode);
+ });
+ }
+ updateRingerH();
+ }
+
+ private void initODICaptionsH() {
+ if (mODICaptionsIcon != null) {
+ mODICaptionsIcon.setOnConfirmedTapListener(() -> {
+ onCaptionIconClicked();
+ Events.writeEvent(Events.EVENT_ODI_CAPTIONS_CLICK);
+ }, mHandler);
+ }
+
+ mController.getCaptionsComponentState(false);
+ }
+
+ private void checkODICaptionsTooltip(boolean fromDismiss) {
+ if (!mHasSeenODICaptionsTooltip && !fromDismiss && mODICaptionsTooltipViewStub != null) {
+ mController.getCaptionsComponentState(true);
+ } else {
+ if (mHasSeenODICaptionsTooltip && fromDismiss && mODICaptionsTooltipView != null) {
+ hideCaptionsTooltip();
+ }
+ }
+ }
+
+ protected void showCaptionsTooltip() {
+ if (!mHasSeenODICaptionsTooltip && mODICaptionsTooltipViewStub != null) {
+ mODICaptionsTooltipView = mODICaptionsTooltipViewStub.inflate();
+ mODICaptionsTooltipView.findViewById(R.id.dismiss).setOnClickListener(v -> {
+ hideCaptionsTooltip();
+ Events.writeEvent(Events.EVENT_ODI_CAPTIONS_TOOLTIP_CLICK);
+ });
+ mODICaptionsTooltipViewStub = null;
+ rescheduleTimeoutH();
+ }
+
+ if (mODICaptionsTooltipView != null) {
+ mODICaptionsTooltipView.setAlpha(0.f);
+ mODICaptionsTooltipView.animate()
+ .alpha(1.f)
+ .setStartDelay(DIALOG_SHOW_ANIMATION_DURATION)
+ .withEndAction(() -> {
+ if (D.BUG) Log.d(TAG, "tool:checkODICaptionsTooltip() putBoolean true");
+ Prefs.putBoolean(mSysUIContext,
+ Prefs.Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP, true);
+ mHasSeenODICaptionsTooltip = true;
+ if (mODICaptionsIcon != null) {
+ mODICaptionsIcon
+ .postOnAnimation(getSinglePressFor(mODICaptionsIcon));
+ }
+ })
+ .start();
+ }
+ }
+
+ private void hideCaptionsTooltip() {
+ if (mODICaptionsTooltipView != null && mODICaptionsTooltipView.getVisibility() == VISIBLE) {
+ mODICaptionsTooltipView.animate().cancel();
+ mODICaptionsTooltipView.setAlpha(1.f);
+ mODICaptionsTooltipView.animate()
+ .alpha(0.f)
+ .setStartDelay(0)
+ .setDuration(DIALOG_HIDE_ANIMATION_DURATION)
+ .withEndAction(() -> mODICaptionsTooltipView.setVisibility(INVISIBLE))
+ .start();
+ }
+ }
+
+ protected void tryToRemoveCaptionsTooltip() {
+ if (mHasSeenODICaptionsTooltip && mODICaptionsTooltipView != null) {
+ ViewGroup container = mDialog.findViewById(R.id.volume_dialog_container);
+ container.removeView(mODICaptionsTooltipView);
+ mODICaptionsTooltipView = null;
+ }
+ }
+
+ private void updateODICaptionsH(boolean isServiceComponentEnabled, boolean fromTooltip) {
+ if (mODICaptionsView != null) {
+ mODICaptionsView.setVisibility(isServiceComponentEnabled ? VISIBLE : GONE);
+ }
+
+ if (!isServiceComponentEnabled) return;
+
+ updateCaptionsIcon();
+ if (fromTooltip) showCaptionsTooltip();
+ }
+
+ private void updateCaptionsIcon() {
+ boolean captionsEnabled = mController.areCaptionsEnabled();
+ if (mODICaptionsIcon.getCaptionsEnabled() != captionsEnabled) {
+ mODICaptionsIcon.setCaptionsEnabled(captionsEnabled);
+ }
+
+ boolean isOptedOut = mController.isCaptionStreamOptedOut();
+ if (mODICaptionsIcon.getOptedOut() != isOptedOut) {
+ mHandler.post(() -> mODICaptionsIcon.setOptedOut(isOptedOut));
+ }
+ }
+
+ private void onCaptionIconClicked() {
+ boolean isEnabled = mController.areCaptionsEnabled();
+ mController.setCaptionsEnabled(!isEnabled);
+ updateCaptionsIcon();
+ }
+
+ private void incrementManualToggleCount() {
+ ContentResolver cr = mContext.getContentResolver();
+ int ringerCount = Settings.Secure.getInt(cr, Settings.Secure.MANUAL_RINGER_TOGGLE_COUNT, 0);
+ Settings.Secure.putInt(cr, Settings.Secure.MANUAL_RINGER_TOGGLE_COUNT, ringerCount + 1);
+ }
+
+ private void provideTouchFeedbackH(int newRingerMode) {
+ VibrationEffect effect = null;
+ switch (newRingerMode) {
+ case RINGER_MODE_NORMAL:
+ mController.scheduleTouchFeedback();
+ break;
+ case RINGER_MODE_SILENT:
+ effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+ break;
+ case RINGER_MODE_VIBRATE:
+ default:
+ effect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
+ }
+ if (effect != null) {
+ mController.vibrate(effect);
+ }
+ }
+
+ private void maybeShowToastH(int newRingerMode) {
+ int seenToastCount = Prefs.getInt(mSysUIContext, Prefs.Key.SEEN_RINGER_GUIDANCE_COUNT, 0);
+
+ if (seenToastCount > VolumePrefs.SHOW_RINGER_TOAST_COUNT) {
+ return;
+ }
+ CharSequence toastText = null;
+ switch (newRingerMode) {
+ case RINGER_MODE_NORMAL:
+ final StreamState ss = mState.states.get(AudioManager.STREAM_RING);
+ if (ss != null) {
+ toastText = mSysUIContext.getString(
+ mSysUIR.string("volume_dialog_ringer_guidance_ring"),
+ Utils.formatPercentage(ss.level, ss.levelMax));
+ }
+ break;
+ case RINGER_MODE_SILENT:
+ toastText = mSysUIContext.getString(
+ com.android.internal.R.string.volume_dialog_ringer_guidance_silent);
+ break;
+ case RINGER_MODE_VIBRATE:
+ default:
+ toastText = mSysUIContext.getString(
+ com.android.internal.R.string.volume_dialog_ringer_guidance_vibrate);
+ }
+
+ Toast.makeText(mContext, toastText, Toast.LENGTH_SHORT).show();
+ seenToastCount++;
+ Prefs.putInt(mSysUIContext, Prefs.Key.SEEN_RINGER_GUIDANCE_COUNT, seenToastCount);
+ }
+
+ public void show(int reason) {
+ mHandler.obtainMessage(H.SHOW, reason, 0).sendToTarget();
+ }
+
+ public void dismiss(int reason) {
+ mHandler.obtainMessage(H.DISMISS, reason, 0).sendToTarget();
+ }
+
+ private void showH(int reason) {
+ if (D.BUG) Log.d(TAG, "showH r=" + Events.SHOW_REASONS[reason]);
+ mHandler.removeMessages(H.SHOW);
+ mHandler.removeMessages(H.DISMISS);
+ rescheduleTimeoutH();
+
+ if (mConfigChanged) {
+ initDialog(); // resets mShowing to false
+ mConfigurableTexts.update();
+ mConfigChanged = false;
+ }
+
+ initSettingsH();
+ mDialog.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsListener);
+
+ if(!mShowing && !mDialog.isShown()) {
+ if (!isLandscape()) mDialogView.setTranslationX((mDialogView.getWidth() / 2.0f)*(isAudioPanelOnLeftSide() ? -1 : 1));
+ mDialogView.setAlpha(0);
+ mDialogView.animate()
+ .alpha(1)
+ .translationX(0)
+ .setDuration(DIALOG_SHOW_ANIMATION_DURATION)
+ .setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator())
+ .withStartAction(() -> {
+ if(!mDialog.isShown()) {
+ mWindowManager.addView(mDialog, mWindowParams);
+ }
+ })
+ .withEndAction(() -> {
+ if (!Prefs.getBoolean(mSysUIContext, Prefs.Key.TOUCHED_RINGER_TOGGLE, false)) {
+ if (mRingerIcon != null) {
+ mRingerIcon.postOnAnimationDelayed(
+ getSinglePressFor(mRingerIcon), 1500);
+ }
+ }
+ mShowing = true;
+ })
+ .start();
+ }
+ Events.writeEvent(Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
+ mController.notifyVisible(true);
+ mController.getCaptionsComponentState(false);
+ checkODICaptionsTooltip(false);
+ }
+
+ protected void rescheduleTimeoutH() {
+ mHandler.removeMessages(H.DISMISS);
+ final int timeout = computeTimeoutH();
+ mHandler.sendMessageDelayed(mHandler
+ .obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT, 0), timeout);
+ if (D.BUG) Log.d(TAG, "rescheduleTimeout " + timeout + " " + Debug.getCaller());
+ mController.userActivity();
+ }
+
+ private int computeTimeoutH() {
+ if (mHovering) {
+ return mAccessibilityMgr.getRecommendedTimeoutMillis(DIALOG_HOVERING_TIMEOUT_MILLIS,
+ AccessibilityManager.FLAG_CONTENT_CONTROLS);
+ }
+ if (mSafetyWarning != null) {
+ return mAccessibilityMgr.getRecommendedTimeoutMillis(
+ DIALOG_SAFETYWARNING_TIMEOUT_MILLIS,
+ AccessibilityManager.FLAG_CONTENT_TEXT
+ | AccessibilityManager.FLAG_CONTENT_CONTROLS);
+ }
+ if (!mHasSeenODICaptionsTooltip && mODICaptionsTooltipView != null) {
+ return mAccessibilityMgr.getRecommendedTimeoutMillis(
+ DIALOG_ODI_CAPTIONS_TOOLTIP_TIMEOUT_MILLIS,
+ AccessibilityManager.FLAG_CONTENT_TEXT
+ | AccessibilityManager.FLAG_CONTENT_CONTROLS);
+ }
+ return mAccessibilityMgr.getRecommendedTimeoutMillis(DIALOG_TIMEOUT_MILLIS,
+ AccessibilityManager.FLAG_CONTENT_CONTROLS);
+ }
+
+ protected void dismissH(int reason) {
+ if (D.BUG) {
+ Log.d(TAG, "mDialog.dismiss() reason: " + Events.DISMISS_REASONS[reason]
+ + " from: " + Debug.getCaller());
+ }
+ if (!mShowing) {
+ // This may happen when dismissing an expanded panel, don't animate again
+ return;
+ }
+ mHandler.removeMessages(H.DISMISS);
+ mHandler.removeMessages(H.SHOW);
+ mDialogView.animate().cancel();
+ if (mShowing) {
+ mShowing = false;
+ // Only logs when the volume dialog visibility is changed.
+ Events.writeEvent(Events.EVENT_DISMISS_DIALOG, reason);
+ }
+ mDialogView.setTranslationX(0);
+ mDialogView.setAlpha(1);
+ ViewPropertyAnimator animator = mDialogView.animate()
+ .alpha(0)
+ .setDuration(DIALOG_HIDE_ANIMATION_DURATION)
+ .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
+ .withEndAction(() -> mHandler.postDelayed(() -> {
+ if(mDialog.isShown()){
+ mWindowManager.removeViewImmediate(mDialog);
+ }
+ cleanExpandedRows();
+ mExpanded = false;
+ mExpandRows.setExpanded(mExpanded);
+ tryToRemoveCaptionsTooltip();
+ }, 50));
+ if (!isLandscape()) animator.translationX((mDialogView.getWidth() / 2.0f)*(isAudioPanelOnLeftSide() ? -1 : 1));
+ animator.start();
+ mDialog.getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsListener);
+ checkODICaptionsTooltip(true);
+ mController.notifyVisible(false);
+ synchronized (mSafetyWarningLock) {
+ if (mSafetyWarning != null) {
+ if (D.BUG) Log.d(TAG, "SafetyWarning dismissed");
+ mSafetyWarning.dismiss();
+ }
+ }
+ }
+
+ private boolean showActiveStreamOnly() {
+ return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)
+ || mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEVISION);
+ }
+
+ private boolean shouldBeVisibleH(VolumeRow row, VolumeRow activeRow) {
+ boolean isActive = row.stream == activeRow.stream;
+
+ if (isActive) {
+ return true;
+ }
+
+ if (!mShowActiveStreamOnly) {
+ if (row.stream == AudioSystem.STREAM_ACCESSIBILITY) {
+ return mShowA11yStream;
+ }
+
+ // if the active row is accessibility, then continue to display previous
+ // active row since accessibility is displayed under it
+ if (activeRow.stream == AudioSystem.STREAM_ACCESSIBILITY &&
+ row.stream == mPrevActiveStream) {
+ return true;
+ }
+
+ if (row.defaultStream) {
+ return activeRow.stream == STREAM_RING
+ || activeRow.stream == STREAM_ALARM
+ || activeRow.stream == STREAM_VOICE_CALL
+ || activeRow.stream == STREAM_ACCESSIBILITY
+ || mDynamic.get(activeRow.stream);
+ }
+ }
+
+ return false;
+ }
+
+ private void updateRowsH(final VolumeRow activeRow) {
+ if (D.BUG) Log.d(TAG, "updateRowsH");
+ if (!mShowing) {
+ trimObsoleteH();
+ }
+ // apply changes to all rows
+ for (final VolumeRow row : mRows) {
+ final boolean isActive = row == activeRow;
+ final boolean shouldBeVisible = shouldBeVisibleH(row, activeRow);
+ if (!mExpanded) {
+ Utils.setVisOrGone(row.view, shouldBeVisible);
+ }
+ if (row.view.isShown()) {
+ updateVolumeRowTintH(row, isActive);
+ ColorStateList mIconTint = ColorStateList.valueOf(mContext.getResources().getColor(R.color.row_icon_dark));
+ ColorStateList mIconTintNormal = ColorStateList.valueOf(mContext.getResources().getColor(R.color.row_icon_white));
+ int rowprogress = row.slider.getProgress();
+ if (rowprogress < 500) {
+ row.icon.setImageTintList(mIconTint);
+ } else {
+ row.icon.setImageTintList(mIconTintNormal);
+ }
+ }
+ }
+ }
+
+ protected void updateRingerH() {
+ if (mState != null) {
+ final StreamState ss = mState.states.get(AudioManager.STREAM_RING);
+ if (ss == null) {
+ return;
+ }
+
+ ColorStateList ringerbackgroundnormal = mContext.getResources().getColorStateList(R.color.ringer_bcg_normal);
+ int RingerMuteT = mContext.getResources().getColor(R.color.ringer_icon_mute);
+ int RingerNormalT = mContext.getResources().getColor(R.color.ringer_icon_normal);
+
+ boolean isZenMuted = mState.zenMode == Global.ZEN_MODE_ALARMS
+ || mState.zenMode == Global.ZEN_MODE_NO_INTERRUPTIONS
+ || (mState.zenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+ && mState.disallowRinger);
+ enableRingerViewsH(!isZenMuted);
+ Drawable ringerDrawable;
+ switch (mState.ringerModeInternal) {
+ case AudioManager.RINGER_MODE_VIBRATE:
+ ringerDrawable = mSysUIContext.getDrawable(
+ mSysUIR.drawable("ic_volume_ringer_vibrate"));
+ addAccessibilityDescription(mRingerIcon, RINGER_MODE_VIBRATE,
+ mSysUIContext.getString(mSysUIR.string("volume_ringer_hint_mute")));
+ mRingerIcon.setTag(Events.ICON_STATE_VIBRATE);
+ mRinger.setBackgroundTintList(null);
+ mRingerIcon.setColorFilter(RingerMuteT);
+ break;
+ case AudioManager.RINGER_MODE_SILENT:
+ ringerDrawable = mSysUIContext.getDrawable(
+ mSysUIR.drawable("ic_volume_ringer_mute"));
+ mRingerIcon.setTag(Events.ICON_STATE_MUTE);
+ addAccessibilityDescription(mRingerIcon, RINGER_MODE_SILENT,
+ mSysUIContext.getString(mSysUIR.string("volume_ringer_hint_unmute")));
+ mRinger.setBackgroundTintList(null);
+ mRingerIcon.setColorFilter(RingerMuteT);
+ break;
+ case AudioManager.RINGER_MODE_NORMAL:
+ default:
+ boolean muted = (mAutomute && ss.level == 0) || ss.muted;
+ if (!isZenMuted && muted) {
+ ringerDrawable = mSysUIContext.getDrawable(
+ mSysUIR.drawable("ic_volume_ringer_mute"));
+ addAccessibilityDescription(mRingerIcon, RINGER_MODE_NORMAL,
+ mSysUIContext.getString(mSysUIR.string("volume_ringer_hint_unmute")));
+ mRingerIcon.setTag(Events.ICON_STATE_MUTE);
+ mRinger.setBackgroundTintList(null);
+ mRingerIcon.setColorFilter(RingerMuteT);
+ } else {
+ ringerDrawable = mSysUIContext.getDrawable(
+ mSysUIR.drawable("ic_volume_ringer"));
+ if (mController.hasVibrator()) {
+ addAccessibilityDescription(mRingerIcon, RINGER_MODE_NORMAL,
+ mSysUIContext.getString(mSysUIR.string("volume_ringer_hint_vibrate")));
+ } else {
+ addAccessibilityDescription(mRingerIcon, RINGER_MODE_NORMAL,
+ mSysUIContext.getString(mSysUIR.string("volume_ringer_hint_mute")));
+ }
+ mRingerIcon.setTag(Events.ICON_STATE_UNMUTE);
+ mRinger.setBackgroundTintList(null);
+ mRingerIcon.setColorFilter(RingerMuteT);
+ }
+ break;
+ }
+ mRingerIcon.setImageDrawable(ringerDrawable);
+ }
+ }
+
+ private void addAccessibilityDescription(View view, int currState, String hintLabel) {
+ int currStateResId;
+ switch (currState) {
+ case RINGER_MODE_SILENT:
+ currStateResId = mSysUIR.string("volume_ringer_status_silent");
+ break;
+ case RINGER_MODE_VIBRATE:
+ currStateResId = mSysUIR.string("volume_ringer_status_vibrate");
+ break;
+ case RINGER_MODE_NORMAL:
+ default:
+ currStateResId = mSysUIR.string("volume_ringer_status_normal");
+ }
+
+ view.setContentDescription(mSysUIContext.getString(currStateResId));
+ view.setAccessibilityDelegate(new AccessibilityDelegate() {
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ info.addAction(new AccessibilityNodeInfo.AccessibilityAction(
+ AccessibilityNodeInfo.ACTION_CLICK, hintLabel));
+ }
+ });
+ }
+
+ /**
+ * Toggles enable state of views in a VolumeRow (not including seekbar or icon)
+ * Hides/shows zen icon
+ * @param enable whether to enable volume row views and hide dnd icon
+ */
+ private void enableVolumeRowViewsH(VolumeRow row, boolean enable) {
+ boolean showDndIcon = !enable;
+ row.dndIcon.setVisibility(showDndIcon ? VISIBLE : GONE);
+ }
+
+ /**
+ * Toggles enable state of footer/ringer views
+ * Hides/shows zen icon
+ * @param enable whether to enable ringer views and hide dnd icon
+ */
+ private void enableRingerViewsH(boolean enable) {
+ if (mRingerIcon != null) {
+ mRingerIcon.setEnabled(enable);
+ }
+ if (mZenIcon != null) {
+ mZenIcon.setVisibility(View.GONE);
+ }
+ }
+
+ private void trimObsoleteH() {
+ if (D.BUG) Log.d(TAG, "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);
+ mDialogRowsView.removeView(row.view);
+ }
+ }
+ }
+
+ protected void onStateChangedH(State state) {
+ if (D.BUG) Log.d(TAG, "onStateChangedH() state: " + state.toString());
+ if (mState != null && state != null
+ && mState.ringerModeInternal != state.ringerModeInternal
+ && state.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE) {
+ mController.vibrate(VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK));
+ }
+
+ 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, mSysUIR.drawable("ic_volume_remote"),
+ mSysUIR.drawable("ic_volume_remote_mute"), true,
+ false, true);
+ }
+ }
+
+ if (mActiveStream != state.activeStream) {
+ mPrevActiveStream = mActiveStream;
+ mActiveStream = state.activeStream;
+ VolumeRow activeRow = getActiveRow();
+ updateRowsH(activeRow);
+ if (mShowing) rescheduleTimeoutH();
+ }
+ for (VolumeRow row : mRows) {
+ updateVolumeRowH(row);
+ }
+ updateRingerH();
+ }
+
+ CharSequence composeWindowTitle() {
+ return mSysUIContext.getString(mSysUIR.string("volume_dialog_title"), getStreamLabelH(getActiveRow().ss));
+ }
+
+ private void updateVolumeRowH(VolumeRow row) {
+ if (D.BUG) Log.i(TAG, "updateVolumeRowH s=" + row.stream);
+ if (mState == null) return;
+ final StreamState ss = mState.states.get(row.stream);
+ if (ss == null) return;
+ row.ss = ss;
+ if (ss.level > ss.levelMin) {
+ row.lastAudibleLevel = ss.level;
+ }
+ if (ss.level == row.requestedLevel) {
+ row.requestedLevel = -1;
+ }
+ final boolean isA11yStream = row.stream == STREAM_ACCESSIBILITY;
+ final boolean isRingStream = row.stream == AudioManager.STREAM_RING;
+ final boolean isSystemStream = row.stream == AudioManager.STREAM_SYSTEM;
+ final boolean isAlarmStream = row.stream == STREAM_ALARM;
+ final boolean isMusicStream = row.stream == AudioManager.STREAM_MUSIC;
+ final boolean isMuted = row.ss.level == row.ss.levelMin;
+ final boolean isRingVibrate = isRingStream
+ && mState.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE;
+ final boolean isRingSilent = isRingStream
+ && mState.ringerModeInternal == AudioManager.RINGER_MODE_SILENT;
+ final boolean isZenPriorityOnly = mState.zenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ final boolean isZenAlarms = mState.zenMode == Global.ZEN_MODE_ALARMS;
+ final boolean isZenNone = mState.zenMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
+ final boolean zenMuted = isZenAlarms ? (isRingStream || isSystemStream)
+ : isZenNone ? (isRingStream || isSystemStream || isAlarmStream || isMusicStream)
+ : isZenPriorityOnly ? ((isAlarmStream && mState.disallowAlarms) ||
+ (isMusicStream && mState.disallowMedia) ||
+ (isRingStream && mState.disallowRinger) ||
+ (isSystemStream && mState.disallowSystem))
+ : false;
+
+ // update slider max
+ final int max = ss.levelMax * 100;
+ final boolean maxChanged = max != row.slider.getMax();
+ if (maxChanged) {
+ row.slider.setMax(max);
+ }
+ // update slider min
+ final int min = ss.levelMin * 100;
+ if (min != row.slider.getMin()) {
+ row.slider.setMin(min);
+ }
+
+ // update header text
+ Utils.setText(row.header, getStreamLabelH(ss));
+ row.slider.setContentDescription(row.header.getText());
+ mConfigurableTexts.add(row.header, ss.name);
+
+ // update icon
+ final boolean iconEnabled = (mAutomute || ss.muteSupported) && !zenMuted;
+ row.icon.setEnabled(iconEnabled);
+ row.icon.setAlpha(iconEnabled ? 1 : 0.5f);
+ final int iconRes =
+ isRingVibrate ? mSysUIR.drawable("ic_volume_ringer_vibrate")
+ : isRingSilent || zenMuted ? row.iconMuteRes
+ : ss.routedToBluetooth ?
+ (ss.muted ? mSysUIR.drawable("ic_volume_media_bt_mute")
+ : mSysUIR.drawable("ic_volume_media_bt"))
+ : mAutomute && ss.level == 0 ? row.iconMuteRes
+ : isMuted ? row.iconMuteRes : row.iconRes;
+ Drawable iconResDrawable = mSysUIContext.getDrawable(iconRes);
+ row.icon.setImageDrawable(iconResDrawable);
+ row.iconState =
+ iconRes == mSysUIR.drawable("ic_volume_ringer_vibrate") ? Events.ICON_STATE_VIBRATE
+ : (iconRes == mSysUIR.drawable("ic_volume_media_bt_mute") || iconRes == row.iconMuteRes)
+ ? Events.ICON_STATE_MUTE
+ : (iconRes == mSysUIR.drawable("ic_volume_media_bt") || iconRes == row.iconRes)
+ ? Events.ICON_STATE_UNMUTE
+ : Events.ICON_STATE_UNKNOWN;
+ if (iconEnabled) {
+ if (isRingStream) {
+ if (isRingVibrate) {
+ row.icon.setContentDescription(mSysUIContext.getString(
+ mSysUIR.string("volume_stream_content_description_unmute"),
+ getStreamLabelH(ss)));
+ } else {
+ if (mController.hasVibrator()) {
+ row.icon.setContentDescription(mSysUIContext.getString(
+ mShowA11yStream
+ ? mSysUIR.string("volume_stream_content_description_vibrate_a11y")
+ : mSysUIR.string("volume_stream_content_description_vibrate"),
+ getStreamLabelH(ss)));
+ } else {
+ row.icon.setContentDescription(mSysUIContext.getString(
+ mShowA11yStream
+ ? mSysUIR.string("volume_stream_content_description_mute_a11y")
+ : mSysUIR.string("volume_stream_content_description_mute"),
+ getStreamLabelH(ss)));
+ }
+ }
+ } else if (isA11yStream) {
+ row.icon.setContentDescription(getStreamLabelH(ss));
+ } else {
+ if (ss.muted || mAutomute && ss.level == 0) {
+ row.icon.setContentDescription(mSysUIContext.getString(
+ mSysUIR.string("volume_stream_content_description_unmute"),
+ getStreamLabelH(ss)));
+ } else {
+ row.icon.setContentDescription(mSysUIContext.getString(
+ mShowA11yStream
+ ? mSysUIR.string("volume_stream_content_description_mute_a11y")
+ : mSysUIR.string("volume_stream_content_description_mute"),
+ getStreamLabelH(ss)));
+ }
+ }
+ } else {
+ row.icon.setContentDescription(getStreamLabelH(ss));
+ }
+
+ // ensure tracking is disabled if zenMuted
+ if (zenMuted) {
+ row.tracking = false;
+ }
+ enableVolumeRowViewsH(row, !zenMuted);
+
+ // update slider
+ final boolean enableSlider = !zenMuted;
+ final int vlevel = row.ss.muted && (!isRingStream && !zenMuted) ? 0
+ : row.ss.level;
+ updateVolumeRowSliderH(row, enableSlider, vlevel, maxChanged);
+ }
+
+ private void updateVolumeRowTintH(VolumeRow row, boolean isActive) {
+ if (isActive) {
+ row.slider.requestFocus();
+ }
+ }
+
+ private void updateVolumeRowSliderH(VolumeRow row, boolean enable, int vlevel, boolean maxChanged) {
+ row.slider.setEnabled(enable);
+ updateVolumeRowTintH(row, row.stream == mActiveStream);
+ if (row.tracking) {
+ return; // don't update if user is sliding
+ }
+ final int progress = row.slider.getProgress();
+ final int level = getImpliedLevel(row.slider, progress);
+ final boolean rowVisible = row.view.getVisibility() == 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
+ }
+ if (vlevel == level) {
+ if (mShowing && rowVisible) {
+ return; // don't clamp if visible
+ }
+ }
+ final int newProgress = vlevel * 100;
+ if (progress != newProgress || maxChanged) {
+ 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, true);
+ }
+ ColorStateList mIconTint = ColorStateList.valueOf(mContext.getResources().getColor(R.color.row_icon_dark));
+ ColorStateList mIconTintNormal = ColorStateList.valueOf(mContext.getResources().getColor(R.color.row_icon_white));
+ if (newProgress < 500) {
+ row.icon.setImageTintList(mIconTint);
+ } else {
+ row.icon.setImageTintList(mIconTintNormal);
+ }
+ }
+ }
+
+ 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 void showSafetyWarningH(int flags) {
+ if ((flags & (AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_SHOW_UI_WARNINGS)) != 0
+ || mShowing) {
+ synchronized (mSafetyWarningLock) {
+ if (mSafetyWarning != null) {
+ return;
+ }
+ mSafetyWarning = new SafetyWarningDialog(mContext, mController.getAudioManager()) {
+ @Override
+ protected void cleanUp() {
+ synchronized (mSafetyWarningLock) {
+ mSafetyWarning = null;
+ }
+ recheckH(null);
+ }
+ };
+ mSafetyWarning.show();
+ }
+ recheckH(null);
+ }
+ rescheduleTimeoutH();
+ }
+
+ private String getStreamLabelH(StreamState ss) {
+ if (ss == null) {
+ return "";
+ }
+ if (ss.remoteLabel != null) {
+ return ss.remoteLabel;
+ }
+ try {
+ return mSysUIContext.getString(ss.name);
+ } catch (Resources.NotFoundException e) {
+ Slog.e(TAG, "Can't find translation for stream " + ss);
+ return "";
+ }
+ }
+
+ private Runnable getSinglePressFor(ImageButton button) {
+ return () -> {
+ if (button != null) {
+ button.setPressed(true);
+ button.postOnAnimationDelayed(getSingleUnpressFor(button), 200);
+ }
+ };
+ }
+
+ private Runnable getSingleUnpressFor(ImageButton button) {
+ return () -> {
+ if (button != null) {
+ button.setPressed(false);
+ }
+ };
+ }
+
+ private final VolumeDialogController.Callbacks mControllerCallbackH
+ = new VolumeDialogController.Callbacks() {
+ @Override
+ public void onShowRequested(int reason) {
+ showH(reason);
+ }
+
+ @Override
+ public void onDismissRequested(int reason) {
+ dismissH(reason);
+ }
+
+ @Override
+ 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() {
+ if(mDialog.isShown()) {
+ mWindowManager.removeViewImmediate(mDialog);
+ }
+ mConfigChanged = true;
+ }
+
+ @Override
+ public void onShowVibrateHint() {
+ if (mSilentMode) {
+ mController.setRingerMode(AudioManager.RINGER_MODE_SILENT, false);
+ }
+ }
+
+ @Override
+ public void onShowSilentHint() {
+ if (mSilentMode) {
+ mController.setRingerMode(AudioManager.RINGER_MODE_NORMAL, false);
+ }
+ }
+
+ @Override
+ public void onShowSafetyWarning(int flags) {
+ showSafetyWarningH(flags);
+ }
+
+ @Override
+ public void onAccessibilityModeChanged(Boolean showA11yStream) {
+ mShowA11yStream = showA11yStream == null ? false : showA11yStream;
+ VolumeRow activeRow = getActiveRow();
+ if (!mShowA11yStream && STREAM_ACCESSIBILITY == activeRow.stream) {
+ dismissH(Events.DISMISS_STREAM_GONE);
+ } else {
+ updateRowsH(activeRow);
+ }
+
+ }
+
+ @Override
+ public void onCaptionComponentStateChanged(
+ Boolean isComponentEnabled, Boolean fromTooltip) {
+ updateODICaptionsH(isComponentEnabled, fromTooltip);
+ }
+ };
+
+ 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;
+ private static final int STATE_CHANGED = 7;
+
+ 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;
+ case STATE_CHANGED: onStateChangedH(mState); break;
+ }
+ }
+ }
+
+ 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) {
+ rescheduleTimeoutH();
+ 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);
+ progress = minProgress;
+ }
+ }
+ final int userLevel = getImpliedLevel(seekBar, progress);
+ if (mRow.ss.level != userLevel || mRow.ss.muted && userLevel > 0) {
+ mRow.userAttempt = SystemClock.uptimeMillis();
+ if (mRow.requestedLevel != userLevel) {
+ mController.setActiveStream(mRow.stream);
+ mController.setStreamVolume(mRow.stream, userLevel);
+ mRow.requestedLevel = userLevel;
+ Events.writeEvent(Events.EVENT_TOUCH_LEVEL_CHANGED, mRow.stream,
+ userLevel);
+ }
+ }
+ ColorStateList mIconTint = ColorStateList.valueOf(mContext.getResources().getColor(R.color.row_icon_dark));
+ ColorStateList mIconTintNormal = ColorStateList.valueOf(mContext.getResources().getColor(R.color.row_icon_white));
+ if (progress < 500) {
+ mRow.icon.setImageTintList(mIconTint);
+ } else {
+ mRow.icon.setImageTintList(mIconTintNormal);
+ }
+ }
+
+ @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();
+ final int userLevel = getImpliedLevel(seekBar, seekBar.getProgress());
+ Events.writeEvent(Events.EVENT_TOUCH_LEVEL_DONE, mRow.stream, userLevel);
+ if (mRow.ss.level != userLevel) {
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(H.RECHECK, mRow),
+ USER_ATTEMPT_GRACE_PERIOD);
+ }
+ }
+ }
+
+ private final class Accessibility extends AccessibilityDelegate {
+ public void init() {
+ mDialogView.setAccessibilityDelegate(this);
+ }
+
+ @Override
+ public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
+ // Activities populate their title here. Follow that example.
+ event.getText().add(composeWindowTitle());
+ return true;
+ }
+
+ @Override
+ public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
+ AccessibilityEvent event) {
+ rescheduleTimeoutH();
+ return super.onRequestSendAccessibilityEvent(host, child, event);
+ }
+ }
+
+ private boolean isAudioPanelOnLeftSide() {
+ return mLeftVolumeRocker;
+ }
+
+ private static class VolumeRow {
+ private View view;
+ private TextView header;
+ private ImageButton icon;
+ private SeekBar slider;
+ private int stream;
+ private StreamState ss;
+ private long userAttempt; // last user-driven slider change
+ private boolean tracking; // tracking slider touch
+ private int requestedLevel = -1; // pending user-requested level via progress changed
+ private int iconRes;
+ private int iconMuteRes;
+ private boolean important;
+ private boolean defaultStream;
+ private ColorStateList cachedTint;
+ private int iconState; // from Events
+ private ObjectAnimator anim; // slider progress animation for non-touch-related updates
+ private int animTargetProgress;
+ private int lastAudibleLevel = 2;
+ private FrameLayout dndIcon;
+ }
+}
diff --git a/VolumePluginCommon/Android.bp b/VolumePluginCommon/Android.bp
index dfc75be..75b3d0d 100644
--- a/VolumePluginCommon/Android.bp
+++ b/VolumePluginCommon/Android.bp
@@ -3,6 +3,7 @@ android_library {
static_libs: [
"SystemUIPluginLib",
"androidx.appcompat_appcompat",
+ "androidx.cardview_cardview",
],
resource_dirs: [
"res",
diff --git a/VolumePluginCommon/res/color/volume_dialog_miui_seekbar_line.xml b/VolumePluginCommon/res/color/volume_dialog_miui_seekbar_line.xml
new file mode 100644
index 0000000..aee14de
--- /dev/null
+++ b/VolumePluginCommon/res/color/volume_dialog_miui_seekbar_line.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:alpha="0.25"
+ android:color="?android:attr/colorAccent"/>
+</selector>
diff --git a/product_packages_volume_panels.mk b/product_packages_volume_panels.mk
index f535ff8..a0899c9 100644
--- a/product_packages_volume_panels.mk
+++ b/product_packages_volume_panels.mk
@@ -5,4 +5,5 @@ PRODUCT_PACKAGES += \
OosPanel \
OreoPanel \
P404Panel \
+ ShapeShiftPanel \
TiledPanel