summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java15
-rw-r--r--packages/SystemUI/res/layout/qs_detail.xml5
-rw-r--r--packages/SystemUI/res/layout/qs_divider.xml20
-rw-r--r--packages/SystemUI/res/layout/qs_footer_impl.xml3
-rw-r--r--packages/SystemUI/res/layout/qs_paged_page.xml2
-rw-r--r--packages/SystemUI/res/layout/qs_panel.xml22
-rw-r--r--packages/SystemUI/res/layout/quick_settings_footer.xml17
-rw-r--r--packages/SystemUI/res/layout/quick_settings_header_info.xml5
-rw-r--r--packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml2
-rw-r--r--packages/SystemUI/res/values-land/dimens.xml1
-rw-r--r--packages/SystemUI/res/values-night/styles.xml5
-rw-r--r--packages/SystemUI/res/values-sw600dp-land/config.xml3
-rw-r--r--packages/SystemUI/res/values-sw600dp-port/dimens.xml21
-rw-r--r--packages/SystemUI/res/values-sw600dp/dimens.xml2
-rw-r--r--packages/SystemUI/res/values-sw900dp-land/dimen.xml23
-rw-r--r--packages/SystemUI/res/values-w550dp-land/config.xml6
-rw-r--r--packages/SystemUI/res/values-w550dp-land/dimens.xml22
-rw-r--r--packages/SystemUI/res/values-w650dp-land/dimens.xml21
-rw-r--r--packages/SystemUI/res/values/config.xml3
-rw-r--r--packages/SystemUI/res/values/dimens.xml7
-rw-r--r--packages/SystemUI/res/values/styles.xml5
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java51
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java67
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFragment.java86
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java440
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java180
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/TileLayout.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/NeverExactlyLinearLayout.kt60
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java3
36 files changed, 825 insertions, 439 deletions
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
index 85a9fec859f3..fffcafbf88fb 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
@@ -34,7 +34,7 @@ public interface QS extends FragmentBase {
String ACTION = "com.android.systemui.action.PLUGIN_QS";
- int VERSION = 7;
+ int VERSION = 8;
String TAG = "QS";
@@ -67,15 +67,12 @@ public interface QS extends FragmentBase {
}
/**
- * We need this to handle nested scrolling for QS..
- * Normally we would do this with requestDisallowInterceptTouchEvent, but when both the
- * scroll containers are using the same touch slop, they try to start scrolling at the
- * same time and NotificationPanelView wins, this lets QS win.
- *
- * TODO: Do this using NestedScroll capabilities.
+ * Should touches from the notification panel be disallowed?
+ * The notification panel might grab any touches rom QS at any time to collapse the shade.
+ * We should disallow that in case we are showing the detail panel.
*/
- default boolean onInterceptTouchEvent(MotionEvent event) {
- return isCustomizing();
+ default boolean disallowPanelTouches() {
+ return isShowingDetail();
}
@ProvidesInterface(version = HeightListener.VERSION)
diff --git a/packages/SystemUI/res/layout/qs_detail.xml b/packages/SystemUI/res/layout/qs_detail.xml
index 294bd50fcf8b..59e1a755d7d2 100644
--- a/packages/SystemUI/res/layout/qs_detail.xml
+++ b/packages/SystemUI/res/layout/qs_detail.xml
@@ -22,6 +22,7 @@
android:background="@drawable/qs_detail_background"
android:clickable="true"
android:orientation="vertical"
+ android:layout_marginTop="@*android:dimen/quick_qs_offset_height"
android:paddingBottom="8dp"
android:visibility="invisible"
android:elevation="4dp"
@@ -44,7 +45,7 @@
android:scaleType="fitXY"
/>
- <com.android.systemui.qs.NonInterceptingScrollView
+ <ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
@@ -54,7 +55,7 @@
android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
- </com.android.systemui.qs.NonInterceptingScrollView>
+ </ScrollView>
<include layout="@layout/qs_detail_buttons" />
diff --git a/packages/SystemUI/res/layout/qs_divider.xml b/packages/SystemUI/res/layout/qs_divider.xml
deleted file mode 100644
index 39d48ea4746e..000000000000
--- a/packages/SystemUI/res/layout/qs_divider.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<View xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:alpha=".12"
- android:background="?android:attr/colorForeground" />
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index ebfd0a0fd537..5c00af5705e9 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -61,7 +61,10 @@
android:clickable="true"
android:gravity="center_vertical"
android:focusable="true"
+ android:singleLine="true"
+ android:ellipsize="end"
android:textAppearance="@style/TextAppearance.QS.Status"
+ android:layout_marginEnd="4dp"
android:visibility="gone"/>
</com.android.keyguard.AlphaOptimizedLinearLayout>
diff --git a/packages/SystemUI/res/layout/qs_paged_page.xml b/packages/SystemUI/res/layout/qs_paged_page.xml
index a8960d9b9437..5c8b2b08324f 100644
--- a/packages/SystemUI/res/layout/qs_paged_page.xml
+++ b/packages/SystemUI/res/layout/qs_paged_page.xml
@@ -20,7 +20,5 @@
android:id="@+id/tile_page"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:paddingStart="@dimen/notification_side_paddings"
- android:paddingEnd="@dimen/notification_side_paddings"
android:clipChildren="false"
android:clipToPadding="false" />
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index cdf84260e399..761ab03ee87e 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -48,18 +48,22 @@
android:clipChildren="false"
android:background="@drawable/qs_bg_gradient" />
-
- <com.android.systemui.qs.QSPanel
- android:id="@+id/quick_settings_panel"
- android:layout_marginTop="@*android:dimen/quick_qs_offset_height"
+ <com.android.systemui.qs.NonInterceptingScrollView
+ android:id="@+id/expanded_qs_scroll_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="4dp"
- android:background="@android:color/transparent"
- android:focusable="true"
- android:accessibilityTraversalBefore="@android:id/edit">
- <include layout="@layout/qs_footer_impl" />
- </com.android.systemui.qs.QSPanel>
+ android:layout_weight="1">
+ <com.android.systemui.qs.QSPanel
+ android:id="@+id/quick_settings_panel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@android:color/transparent"
+ android:focusable="true"
+ android:accessibilityTraversalBefore="@android:id/edit">
+ <include layout="@layout/qs_footer_impl" />
+ </com.android.systemui.qs.QSPanel>
+ </com.android.systemui.qs.NonInterceptingScrollView>
<include layout="@layout/quick_status_bar_expanded_header" />
diff --git a/packages/SystemUI/res/layout/quick_settings_footer.xml b/packages/SystemUI/res/layout/quick_settings_footer.xml
index 15f398aa52e6..e7c7b5fbf890 100644
--- a/packages/SystemUI/res/layout/quick_settings_footer.xml
+++ b/packages/SystemUI/res/layout/quick_settings_footer.xml
@@ -14,8 +14,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
+<com.android.systemui.util.NeverExactlyLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
android:paddingBottom="@dimen/qs_tile_padding_top"
@@ -23,23 +23,28 @@
android:paddingStart="@dimen/qs_footer_padding_start"
android:paddingEnd="@dimen/qs_footer_padding_end"
android:gravity="center_vertical"
+ android:layout_gravity="center_vertical|center_horizontal"
android:background="@android:color/transparent">
- <TextView
+ <com.android.systemui.util.AutoMarqueeTextView
android:id="@+id/footer_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="start"
android:layout_weight="1"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:marqueeRepeatLimit="marquee_forever"
android:textAppearance="@style/TextAppearance.QS.TileLabel"
- style="@style/qs_security_footer"/>
+ android:textColor="?android:attr/textColorPrimary"/>
<ImageView
android:id="@+id/footer_icon"
android:layout_width="@dimen/qs_footer_icon_size"
android:layout_height="@dimen/qs_footer_icon_size"
+ android:layout_marginStart="8dp"
android:contentDescription="@null"
android:src="@drawable/ic_info_outline"
- style="@style/qs_security_footer"/>
+ android:tint="?android:attr/textColorPrimary" />
-</LinearLayout>
+</com.android.systemui.util.NeverExactlyLinearLayout>
diff --git a/packages/SystemUI/res/layout/quick_settings_header_info.xml b/packages/SystemUI/res/layout/quick_settings_header_info.xml
index e6ef9b4b902c..fb82304663aa 100644
--- a/packages/SystemUI/res/layout/quick_settings_header_info.xml
+++ b/packages/SystemUI/res/layout/quick_settings_header_info.xml
@@ -20,11 +20,12 @@
android:layout_height="@dimen/qs_header_tooltip_height"
android:layout_below="@id/quick_status_bar_system_icons"
android:visibility="invisible"
- android:theme="@style/QSHeaderTheme">
+ android:theme="@style/QSHeaderTheme"
+ android:forceHasOverlappingRendering="false">
<com.android.systemui.qs.QSHeaderInfoLayout
android:id="@+id/status_container"
- android:layout_width="match_parent"
+ android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent">
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index abeb33111c40..dc34127496f6 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -29,7 +29,6 @@
android:clipToPadding="false"
android:paddingTop="0dp"
android:paddingEnd="0dp"
- android:paddingBottom="10dp"
android:paddingStart="0dp"
android:elevation="4dp" >
@@ -52,6 +51,7 @@
android:clipChildren="false"
android:clipToPadding="false"
android:focusable="true"
+ android:paddingBottom="10dp"
android:importantForAccessibility="yes" />
<TextView
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index d118d8956f17..2d42ce6faa2c 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -31,7 +31,6 @@
<dimen name="battery_detail_graph_space_bottom">9dp</dimen>
<integer name="quick_settings_num_columns">4</integer>
- <bool name="quick_settings_wide">true</bool>
<dimen name="qs_detail_margin_top">0dp</dimen>
<dimen name="volume_tool_tip_right_margin">136dp</dimen>
diff --git a/packages/SystemUI/res/values-night/styles.xml b/packages/SystemUI/res/values-night/styles.xml
index 50261e1b2139..4fdeb6fa4a92 100644
--- a/packages/SystemUI/res/values-night/styles.xml
+++ b/packages/SystemUI/res/values-night/styles.xml
@@ -29,9 +29,4 @@
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
- <style name="qs_security_footer" parent="@style/qs_theme">
- <item name="android:textColor">#B3FFFFFF</item> <!-- 70% white -->
- <item name="android:tint">#FFFFFFFF</item>
- </style>
-
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml
index c4c467152281..3b00ad1bf0c4 100644
--- a/packages/SystemUI/res/values-sw600dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/config.xml
@@ -15,8 +15,5 @@
~ limitations under the License
-->
<resources>
- <!-- Whether QuickSettings is in a phone landscape -->
- <bool name="quick_settings_wide">false</bool>
-
<integer name="quick_settings_num_columns">3</integer>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp-port/dimens.xml b/packages/SystemUI/res/values-sw600dp-port/dimens.xml
new file mode 100644
index 000000000000..40838f362f5c
--- /dev/null
+++ b/packages/SystemUI/res/values-sw600dp-port/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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>
+ <!-- Size of the panel of large phones on portrait. This shouldn't fill, but have some padding on the side -->
+ <dimen name="notification_panel_width">416dp</dimen>
+
+</resources>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 8f73d231c732..fdf4e3b1b796 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -16,8 +16,6 @@
*/
-->
<resources>
- <!-- Standard notification width + gravity -->
- <dimen name="notification_panel_width">416dp</dimen>
<!-- Diameter of outer shape drawable shown in navbar search-->
<dimen name="navbar_search_outerring_diameter">430dip</dimen>
diff --git a/packages/SystemUI/res/values-sw900dp-land/dimen.xml b/packages/SystemUI/res/values-sw900dp-land/dimen.xml
deleted file mode 100644
index 1e0600ed5fe0..000000000000
--- a/packages/SystemUI/res/values-sw900dp-land/dimen.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (c) 2012, 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>
- <!-- Standard notification width + gravity for tablet large screen device -->
- <dimen name="notification_panel_width">544dp</dimen>
-
-</resources>
-
diff --git a/packages/SystemUI/res/values-w550dp-land/config.xml b/packages/SystemUI/res/values-w550dp-land/config.xml
index 16d5317636a2..a33f1312521f 100644
--- a/packages/SystemUI/res/values-w550dp-land/config.xml
+++ b/packages/SystemUI/res/values-w550dp-land/config.xml
@@ -20,9 +20,5 @@
<!-- These resources are around just to allow their values to be customized
for different hardware and product builds. -->
<resources>
-
- <!-- Whether QuickSettings is in a phone landscape -->
- <bool name="quick_settings_wide">true</bool>
-
- <integer name="quick_settings_num_columns">4</integer>
+ <integer name="quick_settings_num_columns">6</integer>
</resources>
diff --git a/packages/SystemUI/res/values-w550dp-land/dimens.xml b/packages/SystemUI/res/values-w550dp-land/dimens.xml
deleted file mode 100644
index 017ca6987820..000000000000
--- a/packages/SystemUI/res/values-w550dp-land/dimens.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (c) 2016, 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>
- <!-- Standard notification width + gravity -->
- <dimen name="notification_panel_width">544dp</dimen>
-
-</resources>
diff --git a/packages/SystemUI/res/values-w650dp-land/dimens.xml b/packages/SystemUI/res/values-w650dp-land/dimens.xml
new file mode 100644
index 000000000000..108d6cf16fec
--- /dev/null
+++ b/packages/SystemUI/res/values-w650dp-land/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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>
+ <!-- Standard notification width + gravity -->
+ <dimen name="notification_panel_width">644dp</dimen>
+
+</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 00537ff0466d..848cdb1e831c 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -95,9 +95,6 @@
<!-- The maximum number of tiles in the QuickQSPanel -->
<integer name="quick_qs_panel_max_columns">6</integer>
- <!-- Whether QuickSettings is in a phone landscape -->
- <bool name="quick_settings_wide">false</bool>
-
<!-- The number of columns in the QuickSettings -->
<integer name="quick_settings_num_columns">3</integer>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 73d8e9a0d8a7..ab0f876a68b8 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1075,8 +1075,7 @@
<dimen name="edge_margin">8dp</dimen>
<!-- The absolute side margins of quick settings -->
- <dimen name="quick_settings_expanded_bottom_margin">16dp</dimen>
- <dimen name="quick_settings_media_extra_bottom_margin">6dp</dimen>
+ <dimen name="quick_settings_bottom_margin_media">16dp</dimen>
<dimen name="rounded_corner_content_padding">0dp</dimen>
<dimen name="nav_content_padding">0dp</dimen>
<dimen name="nav_quick_scrub_track_edge_padding">24dp</dimen>
@@ -1268,8 +1267,8 @@
<dimen name="qs_media_panel_outer_padding">16dp</dimen>
<dimen name="qs_media_album_size">52dp</dimen>
<dimen name="qs_seamless_icon_size">20dp</dimen>
- <dimen name="qqs_media_spacing">8dp</dimen>
- <dimen name="qqs_horizonal_tile_padding_bottom">8dp</dimen>
+ <dimen name="qqs_media_spacing">16dp</dimen>
+ <dimen name="qs_footer_horizontal_margin">22dp</dimen>
<dimen name="magnification_border_size">5dp</dimen>
<dimen name="magnification_frame_move_short">5dp</dimen>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 39f78bf46028..ed36bdbe1e7e 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -387,11 +387,6 @@
<item name="android:homeAsUpIndicator">@drawable/ic_arrow_back</item>
</style>
- <style name="qs_security_footer" parent="@style/qs_theme">
- <item name="android:textColor">?android:attr/textColorSecondary</item>
- <item name="android:tint">?android:attr/textColorSecondary</item>
- </style>
-
<style name="systemui_theme_remote_input" parent="@android:style/Theme.DeviceDefault.Light">
<item name="android:colorAccent">@color/remote_input_accent</item>
</style>
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index 775a1649702a..b86e1d0503d4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -480,7 +480,17 @@ class MediaHierarchyManager @Inject constructor(
if (inOverlay) {
rootOverlay!!.add(mediaFrame)
} else {
+ // When adding back to the host, let's make sure to reset the bounds.
+ // Usually adding the view will trigger a layout that does this automatically,
+ // but we sometimes suppress this.
targetHost.addView(mediaFrame)
+ val left = targetHost.paddingLeft
+ val top = targetHost.paddingTop
+ mediaFrame.setLeftTopRightBottom(
+ left,
+ top,
+ left + currentBounds.width(),
+ top + currentBounds.height())
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt
index 40d317c7bb22..dc157a8dd257 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt
@@ -139,12 +139,7 @@ class DoubleLineTileLayout(
}
tilesToShow = actualColumns * NUM_LINES
- val interTileSpace = if (actualColumns <= 2) {
- // Extra "column" of padding to be distributed on each end
- (availableWidth - actualColumns * smallTileSize) / actualColumns
- } else {
- (availableWidth - actualColumns * smallTileSize) / (actualColumns - 1)
- }
+ val spacePerTile = availableWidth / actualColumns
for (index in 0 until mRecords.size) {
val tileView = mRecords[index].tileView
@@ -154,15 +149,16 @@ class DoubleLineTileLayout(
tileView.visibility = View.VISIBLE
if (index > 0) tileView.updateAccessibilityOrder(mRecords[index - 1].tileView)
val column = index % actualColumns
- val left = getLeftForColumn(column, interTileSpace, actualColumns <= 2)
+ val left = getLeftForColumn(column, spacePerTile)
val top = if (index < actualColumns) 0 else getTopBottomRow()
tileView.layout(left, top, left + smallTileSize, top + smallTileSize)
}
}
}
- private fun getLeftForColumn(column: Int, interSpace: Int, sideMargin: Boolean): Int {
- return (if (sideMargin) interSpace / 2 else 0) + column * (smallTileSize + interSpace)
+ private fun getLeftForColumn(column: Int, spacePerTile: Int): Int {
+ // Distribute the space evenly among all tiles.
+ return (column * spacePerTile + spacePerTile / 2.0f - smallTileSize / 2.0f).toInt()
}
private fun getTopBottomRow() = smallTileSize + cellMarginVertical
diff --git a/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java b/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java
index c204d94916a4..aa17c4aa79b1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java
@@ -17,6 +17,9 @@ package com.android.systemui.qs;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewParent;
import android.widget.ScrollView;
/**
@@ -24,8 +27,12 @@ import android.widget.ScrollView;
*/
public class NonInterceptingScrollView extends ScrollView {
+ private final int mTouchSlop;
+ private float mDownY;
+
public NonInterceptingScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
+ mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
@Override
@@ -34,10 +41,52 @@ public class NonInterceptingScrollView extends ScrollView {
switch (action) {
case MotionEvent.ACTION_DOWN:
if (canScrollVertically(1)) {
- requestDisallowInterceptTouchEvent(true);
+ // If we can scroll down, make sure we're not intercepted by the parent
+ final ViewParent parent = getParent();
+ if (parent != null) {
+ parent.requestDisallowInterceptTouchEvent(true);
+ }
}
break;
}
return super.onTouchEvent(ev);
}
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ // If there's a touch on this view and we can scroll down, we don't want to be intercepted
+ int action = ev.getActionMasked();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ // If we can scroll down, make sure non of our parents intercepts us.
+ if (canScrollVertically(1)) {
+ final ViewParent parent = getParent();
+ if (parent != null) {
+ parent.requestDisallowInterceptTouchEvent(true);
+ }
+ }
+ mDownY = ev.getY();
+ break;
+ case MotionEvent.ACTION_MOVE: {
+ final int y = (int) ev.getY();
+ final float yDiff = y - mDownY;
+ if (yDiff < -mTouchSlop && !canScrollVertically(1)) {
+ // Don't intercept touches that are overscrolling.
+ return false;
+ }
+ break;
+ }
+ }
+ return super.onInterceptTouchEvent(ev);
+ }
+
+ public int getScrollRange() {
+ int scrollRange = 0;
+ if (getChildCount() > 0) {
+ View child = getChildAt(0);
+ scrollRange = Math.max(0,
+ child.getHeight() - (getHeight() - mPaddingBottom - mPaddingTop));
+ }
+ return scrollRange;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index aaff9ac47ebf..28369925367a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -66,6 +66,10 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
private int mHorizontalClipBound;
private final Rect mClippingRect;
private final UiEventLogger mUiEventLogger = QSEvents.INSTANCE.getQsUiEventsLogger();
+ private int mExcessHeight;
+ private int mLastExcessHeight;
+ private int mMinRows = 1;
+ private int mMaxColumns = TileLayout.NO_MAX_COLUMNS;
public PagedTileLayout(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -195,11 +199,18 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mPages.add((TilePage) LayoutInflater.from(getContext())
- .inflate(R.layout.qs_paged_page, this, false));
+ mPages.add(createTilePage());
mAdapter.notifyDataSetChanged();
}
+ private TilePage createTilePage() {
+ TilePage page = (TilePage) LayoutInflater.from(getContext())
+ .inflate(R.layout.qs_paged_page, this, false);
+ page.setMinRows(mMinRows);
+ page.setMaxColumns(mMaxColumns);
+ return page;
+ }
+
public void setPageIndicator(PageIndicator indicator) {
mPageIndicator = indicator;
mPageIndicator.setNumPages(mPages.size());
@@ -298,8 +309,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
}
while (mPages.size() < numPages) {
if (DEBUG) Log.d(TAG, "Adding page");
- mPages.add((TilePage) LayoutInflater.from(getContext())
- .inflate(R.layout.qs_paged_page, this, false));
+ mPages.add(createTilePage());
}
while (mPages.size() > numPages) {
if (DEBUG) Log.d(TAG, "Removing page");
@@ -342,17 +352,54 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
}
@Override
+ public boolean setMinRows(int minRows) {
+ mMinRows = minRows;
+ boolean changed = false;
+ for (int i = 0; i < mPages.size(); i++) {
+ if (mPages.get(i).setMinRows(minRows)) {
+ changed = true;
+ mDistributeTiles = true;
+ }
+ }
+ return changed;
+ }
+
+ @Override
+ public boolean setMaxColumns(int maxColumns) {
+ mMaxColumns = maxColumns;
+ boolean changed = false;
+ for (int i = 0; i < mPages.size(); i++) {
+ if (mPages.get(i).setMaxColumns(maxColumns)) {
+ changed = true;
+ mDistributeTiles = true;
+ }
+ }
+ return changed;
+ }
+
+ /**
+ * Set the amount of excess space that we gave this view compared to the actual available
+ * height. This is because this view is in a scrollview.
+ */
+ public void setExcessHeight(int excessHeight) {
+ mExcessHeight = excessHeight;
+ }
+
+ @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int nTiles = mTiles.size();
// If we have no reason to recalculate the number of rows, skip this step. In particular,
// if the height passed by its parent is the same as the last time, we try not to remeasure.
- if (mDistributeTiles || mLastMaxHeight != MeasureSpec.getSize(heightMeasureSpec)) {
+ if (mDistributeTiles || mLastMaxHeight != MeasureSpec.getSize(heightMeasureSpec)
+ || mLastExcessHeight != mExcessHeight) {
mLastMaxHeight = MeasureSpec.getSize(heightMeasureSpec);
+ mLastExcessHeight = mExcessHeight;
// Only change the pages if the number of rows or columns (from updateResources) has
// changed or the tiles have changed
- if (mPages.get(0).updateMaxRows(heightMeasureSpec, nTiles) || mDistributeTiles) {
+ int availableHeight = mLastMaxHeight - mExcessHeight;
+ if (mPages.get(0).updateMaxRows(availableHeight, nTiles) || mDistributeTiles) {
mDistributeTiles = false;
distributeTiles();
}
@@ -485,14 +532,6 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
// up.
return Math.max(mColumns * mRows, 1);
}
-
- @Override
- public boolean updateResources() {
- final int sidePadding = getContext().getResources().getDimensionPixelSize(
- R.dimen.notification_side_paddings);
- setPadding(sidePadding, 0, sidePadding, 0);
- return super.updateResources();
- }
}
private final PagerAdapter mAdapter = new PagerAdapter() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index ce002297e1a1..bc8f5a8fb652 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -18,6 +18,7 @@ import android.util.Log;
import android.view.View;
import android.view.View.OnAttachStateChangeListener;
import android.view.View.OnLayoutChangeListener;
+import android.widget.ScrollView;
import com.android.systemui.Dependency;
import com.android.systemui.plugins.qs.QS;
@@ -30,7 +31,6 @@ import com.android.systemui.qs.TouchAnimator.Builder;
import com.android.systemui.qs.TouchAnimator.Listener;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
-import com.android.systemui.util.Utils;
import java.util.ArrayList;
import java.util.Collection;
@@ -66,6 +66,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
private TouchAnimator mNonfirstPageAnimator;
private TouchAnimator mNonfirstPageDelayedAnimator;
private TouchAnimator mBrightnessAnimator;
+ private boolean mNeedsAnimatorUpdate = false;
private boolean mOnKeyguard;
@@ -98,6 +99,12 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
updateAnimators();
}
+
+ public void onQsScrollingChanged() {
+ // Lazily update animators whenever the scrolling changes
+ mNeedsAnimatorUpdate = true;
+ }
+
public void setOnKeyguard(boolean onKeyguard) {
mOnKeyguard = onKeyguard;
updateQQSVisibility();
@@ -172,6 +179,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
}
private void updateAnimators() {
+ mNeedsAnimatorUpdate = false;
TouchAnimator.Builder firstPageBuilder = new Builder();
TouchAnimator.Builder translationXBuilder = new Builder();
TouchAnimator.Builder translationYBuilder = new Builder();
@@ -286,13 +294,16 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
.setListener(this)
.build();
// Fade in the tiles/labels as we reach the final position.
- mFirstPageDelayedAnimator = new TouchAnimator.Builder()
+ Builder builder = new Builder()
.setStartDelay(EXPANDED_TILE_DELAY)
- .addFloat(tileLayout, "alpha", 0, 1)
- .addFloat(mQsPanel.getDivider(), "alpha", 0, 1)
- .addFloat(mQsPanel.getFooter().getView(), "alpha", 0, 1).build();
- mAllViews.add(mQsPanel.getDivider());
- mAllViews.add(mQsPanel.getFooter().getView());
+ .addFloat(tileLayout, "alpha", 0, 1);
+ if (mQsPanel.getSecurityFooter() != null) {
+ builder.addFloat(mQsPanel.getSecurityFooter().getView(), "alpha", 0, 1);
+ }
+ mFirstPageDelayedAnimator = builder.build();
+ if (mQsPanel.getSecurityFooter() != null) {
+ mAllViews.add(mQsPanel.getSecurityFooter().getView());
+ }
float px = 0;
float py = 1;
if (tiles.size() <= 3) {
@@ -308,7 +319,6 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
}
mNonfirstPageAnimator = new TouchAnimator.Builder()
.addFloat(mQuickQsPanel, "alpha", 1, 0)
- .addFloat(mQsPanel.getDivider(), "alpha", 0, 1)
.setListener(mNonFirstPageListener)
.setEndDelay(.5f)
.build();
@@ -339,10 +349,18 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
loc1[0] += view.getLeft();
loc1[1] += view.getTop();
}
+ if (!(view instanceof PagedTileLayout)) {
+ // Remove the scrolling position of all scroll views other than the viewpager
+ loc1[0] -= view.getScrollX();
+ loc1[1] -= view.getScrollY();
+ }
getRelativePositionInt(loc1, (View) view.getParent(), parent);
}
public void setPosition(float position) {
+ if (mNeedsAnimatorUpdate) {
+ updateAnimators();
+ }
if (mFirstPageAnimator == null) return;
if (mOnKeyguard) {
if (mShowCollapsedOnKeyguard) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 1c3b6850afc1..0332bc3e0618 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -43,6 +43,7 @@ public class QSContainerImpl extends FrameLayout {
private float mQsExpansion;
private QSCustomizer mQSCustomizer;
private View mDragHandle;
+ private View mQSPanelContainer;
private View mBackground;
private View mBackgroundGradient;
@@ -61,6 +62,7 @@ public class QSContainerImpl extends FrameLayout {
protected void onFinishInflate() {
super.onFinishInflate();
mQSPanel = findViewById(R.id.quick_settings_panel);
+ mQSPanelContainer = findViewById(R.id.expanded_qs_scroll_view);
mQSDetail = findViewById(R.id.qs_detail);
mHeader = findViewById(R.id.header);
mQSCustomizer = findViewById(R.id.qs_customize);
@@ -95,7 +97,7 @@ public class QSContainerImpl extends FrameLayout {
Configuration config = getResources().getConfiguration();
boolean navBelow = config.smallestScreenWidthDp >= 600
|| config.orientation != Configuration.ORIENTATION_LANDSCAPE;
- MarginLayoutParams layoutParams = (MarginLayoutParams) mQSPanel.getLayoutParams();
+ MarginLayoutParams layoutParams = (MarginLayoutParams) mQSPanelContainer.getLayoutParams();
// The footer is pinned to the bottom of QSPanel (same bottoms), therefore we don't need to
// subtract its height. We do not care if the collapsed notifications fit in the screen.
@@ -109,12 +111,11 @@ public class QSContainerImpl extends FrameLayout {
+ layoutParams.rightMargin;
final int qsPanelWidthSpec = getChildMeasureSpec(widthMeasureSpec, padding,
layoutParams.width);
- // Measure with EXACTLY. That way, PagedTileLayout will only use excess height and will be
- // measured last, after other views and padding is accounted for.
- mQSPanel.measure(qsPanelWidthSpec, MeasureSpec.makeMeasureSpec(maxQs, MeasureSpec.EXACTLY));
- int width = mQSPanel.getMeasuredWidth() + padding;
+ mQSPanelContainer.measure(qsPanelWidthSpec,
+ MeasureSpec.makeMeasureSpec(maxQs, MeasureSpec.AT_MOST));
+ int width = mQSPanelContainer.getMeasuredWidth() + padding;
int height = layoutParams.topMargin + layoutParams.bottomMargin
- + mQSPanel.getMeasuredHeight() + getPaddingBottom();
+ + mQSPanelContainer.getMeasuredHeight() + getPaddingBottom();
super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
// QSCustomizer will always be the height of the screen, but do this after
@@ -130,7 +131,7 @@ public class QSContainerImpl extends FrameLayout {
// Do not measure QSPanel again when doing super.onMeasure.
// This prevents the pages in PagedTileLayout to be remeasured with a different (incorrect)
// size to the one used for determining the number of rows and then the number of pages.
- if (child != mQSPanel) {
+ if (child != mQSPanelContainer) {
super.measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed,
parentHeightMeasureSpec, heightUsed);
}
@@ -151,10 +152,10 @@ public class QSContainerImpl extends FrameLayout {
}
private void updateResources() {
- LayoutParams layoutParams = (LayoutParams) mQSPanel.getLayoutParams();
+ LayoutParams layoutParams = (LayoutParams) mQSPanelContainer.getLayoutParams();
layoutParams.topMargin = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.quick_qs_offset_height);
- mQSPanel.setLayoutParams(layoutParams);
+ mQSPanelContainer.setLayoutParams(layoutParams);
mSideMargins = getResources().getDimensionPixelSize(R.dimen.notification_side_paddings);
mContentPaddingStart = getResources().getDimensionPixelSize(
@@ -185,7 +186,7 @@ public class QSContainerImpl extends FrameLayout {
mQSDetail.setBottom(getTop() + height);
// Pin the drag handle to the bottom of the panel.
mDragHandle.setTranslationY(height - mDragHandle.getHeight());
- mBackground.setTop(mQSPanel.getTop());
+ mBackground.setTop(mQSPanelContainer.getTop());
mBackground.setBottom(height);
}
@@ -223,7 +224,7 @@ public class QSContainerImpl extends FrameLayout {
LayoutParams lp = (LayoutParams) view.getLayoutParams();
lp.rightMargin = mSideMargins;
lp.leftMargin = mSideMargins;
- if (view == mQSPanel) {
+ if (view == mQSPanelContainer) {
// QS panel lays out some of its content full width
mQSPanel.setContentMargins(mContentPaddingStart, mContentPaddingEnd);
} else if (view == mHeader) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index dd02edd506b8..f1bb8996e181 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -24,7 +24,6 @@ import android.os.Bundle;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
-import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
@@ -72,6 +71,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
protected QuickStatusBarHeader mHeader;
private QSCustomizer mQSCustomizer;
protected QSPanel mQSPanel;
+ protected NonInterceptingScrollView mQSPanelScrollView;
private QSDetail mQSDetail;
private boolean mListening;
private QSContainerImpl mContainer;
@@ -122,8 +122,20 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mQSPanel = view.findViewById(R.id.quick_settings_panel);
+ mQSPanelScrollView = view.findViewById(R.id.expanded_qs_scroll_view);
+ mQSPanelScrollView.addOnLayoutChangeListener(
+ (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+ updateQsBounds();
+ });
+ mQSPanelScrollView.setOnScrollChangeListener(
+ (v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
+ // Lazily update animators whenever the scrolling changes
+ mQSAnimator.onQsScrollingChanged();
+ mHeader.setExpandedScrollAmount(scrollY);
+ });
mQSDetail = view.findViewById(R.id.qs_detail);
mHeader = view.findViewById(R.id.header);
+ mQSPanel.setHeaderContainer(view.findViewById(R.id.header_text_container));
mFooter = view.findViewById(R.id.qs_footer);
mContainer = view.findViewById(id.quick_settings_container);
@@ -133,8 +145,8 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
mQSDetail.setQsPanel(mQSPanel, mHeader, (View) mFooter);
- mQSAnimator = new QSAnimator(this,
- mHeader.findViewById(R.id.quick_qs_panel), mQSPanel);
+ mQSAnimator = new QSAnimator(this, mHeader.findViewById(R.id.quick_qs_panel), mQSPanel);
+
mQSCustomizer = view.findViewById(R.id.qs_customize);
mQSCustomizer.setQs(this);
@@ -319,11 +331,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
}
@Override
- public boolean onInterceptTouchEvent(MotionEvent event) {
- return isCustomizing();
- }
-
- @Override
public void setHeaderClickable(boolean clickable) {
if (DEBUG) Log.d(TAG, "setHeaderClickable " + clickable);
}
@@ -394,7 +401,9 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
mLastViewHeight = currentHeight;
boolean fullyExpanded = expansion == 1;
- int heightDiff = mQSPanel.getBottom() - mHeader.getBottom() + mHeader.getPaddingBottom();
+ boolean fullyCollapsed = expansion == 0.0f;
+ int heightDiff = mQSPanelScrollView.getBottom() - mHeader.getBottom()
+ + mHeader.getPaddingBottom();
float panelTranslationY = translationScaleY * heightDiff;
// Let the views animate their contents correctly by giving them the necessary context.
@@ -403,19 +412,19 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
mFooter.setExpansion(onKeyguardAndExpanded ? 1 : expansion);
mQSPanel.getQsTileRevealController().setExpansion(expansion);
mQSPanel.getTileLayout().setExpansion(expansion);
- mQSPanel.setTranslationY(translationScaleY * heightDiff);
+ mQSPanelScrollView.setTranslationY(translationScaleY * heightDiff);
+ if (fullyCollapsed) {
+ mQSPanelScrollView.setScrollY(0);
+ }
mQSDetail.setFullyExpanded(fullyExpanded);
- if (fullyExpanded) {
- // Always draw within the bounds of the view when fully expanded.
- mQSPanel.setClipBounds(null);
- } else {
+ if (!fullyExpanded) {
// Set bounds on the QS panel so it doesn't run over the header when animating.
- mQsBounds.top = (int) -mQSPanel.getTranslationY();
- mQsBounds.right = mQSPanel.getWidth();
- mQsBounds.bottom = mQSPanel.getHeight();
- mQSPanel.setClipBounds(mQsBounds);
+ mQsBounds.top = (int) -mQSPanelScrollView.getTranslationY();
+ mQsBounds.right = mQSPanelScrollView.getWidth();
+ mQsBounds.bottom = mQSPanelScrollView.getHeight();
}
+ updateQsBounds();
if (mQSAnimator != null) {
mQSAnimator.setPosition(expansion);
@@ -423,24 +432,34 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
updateMediaPositions();
}
+ private void updateQsBounds() {
+ if (mLastQSExpansion == 1.0f) {
+ // Fully expanded, let's set the layout bounds as clip bounds. This is necessary because
+ // it's a scrollview and otherwise wouldn't be clipped.
+ mQsBounds.set(0, 0, mQSPanelScrollView.getWidth(), mQSPanelScrollView.getHeight());
+ }
+ mQSPanelScrollView.setClipBounds(mQsBounds);
+ }
+
private void updateMediaPositions() {
if (Utils.useQsMediaPlayer(getContext())) {
mContainer.getLocationOnScreen(mTmpLocation);
float absoluteBottomPosition = mTmpLocation[1] + mContainer.getHeight();
+ // The Media can be scrolled off screen by default, let's offset it
+ float expandedMediaPosition = absoluteBottomPosition - mQSPanelScrollView.getScrollY()
+ + mQSPanelScrollView.getScrollRange();
// The expanded media host should never move below the laid out position
- pinToBottom(absoluteBottomPosition, mQSPanel.getMediaHost(), true /* expanded */);
+ pinToBottom(expandedMediaPosition, mQSPanel.getMediaHost(), true /* expanded */);
// The expanded media host should never move above the laid out position
- pinToBottom(absoluteBottomPosition - mHeader.getPaddingBottom(),
- mHeader.getHeaderQsPanel().getMediaHost(), false /* expanded */);
+ pinToBottom(absoluteBottomPosition, mHeader.getHeaderQsPanel().getMediaHost(),
+ false /* expanded */);
}
}
private void pinToBottom(float absoluteBottomPosition, MediaHost mediaHost, boolean expanded) {
View hostView = mediaHost.getHostView();
if (mLastQSExpansion > 0) {
- ViewGroup.MarginLayoutParams params =
- (ViewGroup.MarginLayoutParams) hostView.getLayoutParams();
- float targetPosition = absoluteBottomPosition - params.bottomMargin
+ float targetPosition = absoluteBottomPosition - getTotalBottomMargin(hostView)
- hostView.getHeight();
float currentPosition = mediaHost.getCurrentBounds().top
- hostView.getTranslationY();
@@ -458,6 +477,18 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
}
}
+ private float getTotalBottomMargin(View startView) {
+ int result = 0;
+ View child = startView;
+ View parent = (View) startView.getParent();
+ while (!(parent instanceof QSContainerImpl) && parent != null) {
+ result += parent.getHeight() - child.getBottom();
+ child = parent;
+ parent = (View) parent.getParent();
+ }
+ return result;
+ }
+
private boolean headerWillBeAnimating() {
return mState == StatusBarState.KEYGUARD && mShowCollapsedOnKeyguard
&& !isKeyguardShowing();
@@ -514,7 +545,8 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
public void notifyCustomizeChanged() {
// The customize state changed, so our height changed.
mContainer.updateExpansion();
- mQSPanel.setVisibility(!mQSCustomizer.isCustomizing() ? View.VISIBLE : View.INVISIBLE);
+ mQSPanelScrollView.setVisibility(!mQSCustomizer.isCustomizing() ? View.VISIBLE
+ : View.INVISIBLE);
mFooter.setVisibility(!mQSCustomizer.isCustomizing() ? View.VISIBLE : View.INVISIBLE);
// Let the panel know the position changed and it needs to update where notifications
// and whatnot are.
@@ -531,9 +563,9 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
return getView().getHeight();
}
if (mQSDetail.isClosingDetail()) {
- LayoutParams layoutParams = (LayoutParams) mQSPanel.getLayoutParams();
+ LayoutParams layoutParams = (LayoutParams) mQSPanelScrollView.getLayoutParams();
int panelHeight = layoutParams.topMargin + layoutParams.bottomMargin +
- + mQSPanel.getMeasuredHeight();
+ + mQSPanelScrollView.getMeasuredHeight();
return panelHeight + getView().getPaddingBottom();
} else {
return getView().getMeasuredHeight();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 78448785fe2f..ecdb2c91ca48 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -16,10 +16,10 @@
package com.android.systemui.qs;
-import static com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState;
import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
import static com.android.systemui.util.Utils.useQsMediaPlayer;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
@@ -29,8 +29,8 @@ import android.metrics.LogMaker;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
-import android.service.quicksettings.Tile;
import android.util.AttributeSet;
+import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -39,7 +39,7 @@ import android.widget.LinearLayout;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settingslib.Utils;
+import com.android.internal.widget.RemeasuringLinearLayout;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
@@ -83,38 +83,65 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
private final BroadcastDispatcher mBroadcastDispatcher;
protected final MediaHost mMediaHost;
+
+ /**
+ * The index where the content starts that needs to be moved between parents
+ */
+ private final int mMovableContentStartIndex;
private String mCachedSpecs = "";
- protected final View mBrightnessView;
+
+ @Nullable
+ protected View mBrightnessView;
+ @Nullable
+ private BrightnessController mBrightnessController;
+
private final H mHandler = new H();
private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
- private final QSTileRevealController mQsTileRevealController;
+ private QSTileRevealController mQsTileRevealController;
/** Whether or not the QS media player feature is enabled. */
protected boolean mUsingMediaPlayer;
+ private int mVisualMarginStart;
+ private int mVisualMarginEnd;
protected boolean mExpanded;
protected boolean mListening;
private QSDetail.Callback mCallback;
- private BrightnessController mBrightnessController;
private final DumpManager mDumpManager;
private final QSLogger mQSLogger;
protected final UiEventLogger mUiEventLogger;
protected QSTileHost mHost;
- protected QSSecurityFooter mFooter;
+ @Nullable
+ protected QSSecurityFooter mSecurityFooter;
+
+ @Nullable
+ protected View mFooter;
+
+ @Nullable
+ private ViewGroup mHeaderContainer;
private PageIndicator mFooterPageIndicator;
private boolean mGridContentVisible = true;
private int mContentMarginStart;
private int mContentMarginEnd;
private int mVisualTilePadding;
-
- protected QSTileLayout mTileLayout;
+ private boolean mUsingHorizontalLayout;
private QSCustomizer mCustomizePanel;
private Record mDetailRecord;
private BrightnessMirrorController mBrightnessMirrorController;
- private View mDivider;
+ private LinearLayout mHorizontalLinearLayout;
+ private LinearLayout mHorizontalContentContainer;
+
+ // Only used with media
+ private QSTileLayout mHorizontalTileLayout;
+ protected QSTileLayout mRegularTileLayout;
+ protected QSTileLayout mTileLayout;
+ private int mLastOrientation = -1;
+ private int mMediaTotalBottomMargin;
+ private int mFooterMarginStartHorizontal;
+
@Inject
public QSPanel(
@@ -128,7 +155,13 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
) {
super(context, attrs);
mUsingMediaPlayer = useQsMediaPlayer(context);
+ mMediaTotalBottomMargin = getResources().getDimensionPixelSize(
+ R.dimen.quick_settings_bottom_margin_media);
mMediaHost = mediaHost;
+ mMediaHost.setVisibleChangedListener((visible) -> {
+ switchTileLayout();
+ return null;
+ });
mContext = context;
mQSLogger = qsLogger;
mDumpManager = dumpManager;
@@ -137,71 +170,97 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
setOrientation(VERTICAL);
- mBrightnessView = LayoutInflater.from(mContext).inflate(
- R.layout.quick_settings_brightness_dialog, this, false);
- addView(mBrightnessView);
-
- mTileLayout = (QSTileLayout) LayoutInflater.from(mContext).inflate(
- R.layout.qs_paged_tile_layout, this, false);
+ addViewsAboveTiles();
+ mMovableContentStartIndex = getChildCount();
+ mRegularTileLayout = createRegularTileLayout();
+
+ if (mUsingMediaPlayer) {
+ mHorizontalLinearLayout = new RemeasuringLinearLayout(mContext);
+ mHorizontalLinearLayout.setOrientation(LinearLayout.HORIZONTAL);
+ mHorizontalLinearLayout.setClipChildren(false);
+ mHorizontalLinearLayout.setClipToPadding(false);
+
+ mHorizontalContentContainer = new RemeasuringLinearLayout(mContext);
+ mHorizontalContentContainer.setOrientation(LinearLayout.VERTICAL);
+ mHorizontalContentContainer.setClipChildren(false);
+ mHorizontalContentContainer.setClipToPadding(false);
+
+ mHorizontalTileLayout = createHorizontalTileLayout();
+ LayoutParams lp = new LayoutParams(0, LayoutParams.WRAP_CONTENT, 1);
+ int marginSize = (int) mContext.getResources().getDimension(R.dimen.qqs_media_spacing);
+ lp.setMarginStart(0);
+ lp.setMarginEnd(marginSize);
+ lp.gravity = Gravity.CENTER_VERTICAL;
+ mHorizontalLinearLayout.addView(mHorizontalContentContainer, lp);
+
+ lp = new LayoutParams(LayoutParams.MATCH_PARENT, 0, 1);
+ addView(mHorizontalLinearLayout, lp);
+
+ initMediaHostState();
+ }
+ addSecurityFooter();
+ if (mRegularTileLayout instanceof PagedTileLayout) {
+ mQsTileRevealController = new QSTileRevealController(mContext, this,
+ (PagedTileLayout) mRegularTileLayout);
+ }
mQSLogger.logAllTilesChangeListening(mListening, getDumpableTag(), mCachedSpecs);
- mTileLayout.setListening(mListening);
- addView((View) mTileLayout);
-
- mQsTileRevealController = new QSTileRevealController(mContext, this,
- (PagedTileLayout) mTileLayout);
-
- addDivider();
-
- mFooter = new QSSecurityFooter(this, context);
- addView(mFooter.getView());
-
updateResources();
+ }
+ protected void addSecurityFooter() {
+ mSecurityFooter = new QSSecurityFooter(this, mContext);
+ }
+
+ protected void addViewsAboveTiles() {
+ mBrightnessView = LayoutInflater.from(mContext).inflate(
+ R.layout.quick_settings_brightness_dialog, this, false);
+ addView(mBrightnessView);
mBrightnessController = new BrightnessController(getContext(),
findViewById(R.id.brightness_slider), mBroadcastDispatcher);
}
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- // Add media carousel at the end
- if (useQsMediaPlayer(getContext())) {
- addMediaHostView();
+ protected QSTileLayout createRegularTileLayout() {
+ if (mRegularTileLayout == null) {
+ mRegularTileLayout = (QSTileLayout) LayoutInflater.from(mContext).inflate(
+ R.layout.qs_paged_tile_layout, this, false);
}
+ return mRegularTileLayout;
+ }
+
+
+ protected QSTileLayout createHorizontalTileLayout() {
+ return createRegularTileLayout();
}
- protected void addMediaHostView() {
+ protected void initMediaHostState() {
mMediaHost.setExpansion(1.0f);
mMediaHost.setShowsOnlyActiveMedia(false);
mMediaHost.init(MediaHierarchyManager.LOCATION_QS);
- ViewGroup hostView = mMediaHost.getHostView();
- addView(hostView);
- int bottomPadding = getResources().getDimensionPixelSize(
- R.dimen.quick_settings_expanded_bottom_margin);
- MarginLayoutParams layoutParams = (MarginLayoutParams) hostView.getLayoutParams();
- layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
- layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
- layoutParams.bottomMargin = bottomPadding;
- hostView.setLayoutParams(layoutParams);
- updateMediaHostContentMargins();
- }
-
- protected void addDivider() {
- mDivider = LayoutInflater.from(mContext).inflate(R.layout.qs_divider, this, false);
- mDivider.setBackgroundColor(Utils.applyAlpha(mDivider.getAlpha(),
- getColorForState(mContext, Tile.STATE_ACTIVE)));
- addView(mDivider);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+
+ if (mTileLayout instanceof PagedTileLayout) {
+ // Allow the UI to be as big as it want's to, we're in a scroll view
+ int newHeight = 10000;
+ int availableHeight = MeasureSpec.getSize(heightMeasureSpec);
+ int excessHeight = newHeight - availableHeight;
+ // Measure with EXACTLY. That way, The content will only use excess height and will
+ // be measured last, after other views and padding is accounted for. This only
+ // works because our Layouts in here remeasure themselves with the exact content
+ // height.
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY);
+ ((PagedTileLayout) mTileLayout).setExcessHeight(excessHeight);
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
// We want all the logic of LinearLayout#onMeasure, and for it to assign the excess space
// not used by the other children to PagedTileLayout. However, in this case, LinearLayout
// assumes that PagedTileLayout would use all the excess space. This is not the case as
// PagedTileLayout height is quantized (because it shows a certain number of rows).
// Therefore, after everything is measured, we need to make sure that we add up the correct
// total height
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int height = getPaddingBottom() + getPaddingTop();
int numChildren = getChildCount();
for (int i = 0; i < numChildren; i++) {
@@ -215,10 +274,6 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
setMeasuredDimension(getMeasuredWidth(), height);
}
- public View getDivider() {
- return mDivider;
- }
-
public QSTileRevealController getQsTileRevealController() {
return mQsTileRevealController;
}
@@ -273,7 +328,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
@Override
public void onTuningChanged(String key, String newValue) {
- if (QS_SHOW_BRIGHTNESS.equals(key)) {
+ if (QS_SHOW_BRIGHTNESS.equals(key) && mBrightnessView != null) {
updateViewVisibilityForTuningValue(mBrightnessView, newValue);
}
}
@@ -316,6 +371,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
updateBrightnessMirror();
}
+ @Nullable
View getBrightnessView() {
return mBrightnessView;
}
@@ -328,7 +384,9 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
mHost = host;
mHost.addCallback(this);
setTiles(mHost.getTiles());
- mFooter.setHostEnvironment(host);
+ if (mSecurityFooter != null) {
+ mSecurityFooter.setHostEnvironment(host);
+ }
mCustomizePanel = customizer;
if (mCustomizePanel != null) {
mCustomizePanel.setHost(mHost);
@@ -341,18 +399,18 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
* @param pageIndicator indicator to use for page scrolling
*/
public void setFooterPageIndicator(PageIndicator pageIndicator) {
- if (mTileLayout instanceof PagedTileLayout) {
+ if (mRegularTileLayout instanceof PagedTileLayout) {
mFooterPageIndicator = pageIndicator;
updatePageIndicator();
}
}
private void updatePageIndicator() {
- if (mTileLayout instanceof PagedTileLayout) {
+ if (mRegularTileLayout instanceof PagedTileLayout) {
if (mFooterPageIndicator != null) {
mFooterPageIndicator.setVisibility(View.GONE);
- ((PagedTileLayout) mTileLayout).setPageIndicator(mFooterPageIndicator);
+ ((PagedTileLayout) mRegularTileLayout).setPageIndicator(mFooterPageIndicator);
}
}
}
@@ -364,6 +422,8 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
public void updateResources() {
int tileSize = getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size);
int tileBg = getResources().getDimensionPixelSize(R.dimen.qs_tile_background_size);
+ mFooterMarginStartHorizontal = getResources().getDimensionPixelSize(
+ R.dimen.qs_footer_horizontal_margin);
mVisualTilePadding = (int) ((tileSize - tileBg) / 2.0f);
updatePadding();
@@ -379,8 +439,15 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
protected void updatePadding() {
final Resources res = mContext.getResources();
+ int padding = res.getDimensionPixelSize(R.dimen.qs_panel_padding_top);
+ if (mUsingHorizontalLayout) {
+ // When using the horizontal layout, our space is quite constrained. We therefore
+ // reduce some of the padding on the top, which makes the brightness bar overlapp,
+ // but since that has naturally quite a bit of built in padding, that's fine.
+ padding = (int) (padding * 0.6f);
+ }
setPaddingRelative(getPaddingStart(),
- res.getDimensionPixelSize(R.dimen.qs_panel_padding_top),
+ padding,
getPaddingEnd(),
res.getDimensionPixelSize(R.dimen.qs_panel_padding_bottom));
}
@@ -388,10 +455,165 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- mFooter.onConfigurationChanged();
+ if (mSecurityFooter != null) {
+ mSecurityFooter.onConfigurationChanged();
+ }
updateResources();
updateBrightnessMirror();
+
+ if (newConfig.orientation != mLastOrientation) {
+ mLastOrientation = newConfig.orientation;
+ switchTileLayout();
+ }
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mFooter = findViewById(R.id.qs_footer);
+ switchTileLayout(true /* force */);
+ }
+
+ boolean switchTileLayout() {
+ return switchTileLayout(false /* force */);
+ }
+
+ private boolean switchTileLayout(boolean force) {
+ /** Whether or not the QuickQSPanel currently contains a media player. */
+ boolean horizontal = shouldUseHorizontalLayout();
+ if (horizontal != mUsingHorizontalLayout || force) {
+ mUsingHorizontalLayout = horizontal;
+ View visibleView = horizontal ? mHorizontalLinearLayout : (View) mRegularTileLayout;
+ View hiddenView = horizontal ? (View) mRegularTileLayout : mHorizontalLinearLayout;
+ ViewGroup newParent = horizontal ? mHorizontalContentContainer : this;
+ QSTileLayout newLayout = horizontal ? mHorizontalTileLayout : mRegularTileLayout;
+ if (hiddenView != null &&
+ (mRegularTileLayout != mHorizontalTileLayout ||
+ hiddenView != mRegularTileLayout)) {
+ // Only hide the view if the horizontal and the regular view are different,
+ // otherwise its reattached.
+ hiddenView.setVisibility(View.GONE);
+ }
+ visibleView.setVisibility(View.VISIBLE);
+ switchAllContentToParent(newParent, newLayout);
+ reAttachMediaHost();
+ if (mTileLayout != null) {
+ mTileLayout.setListening(false);
+ for (TileRecord record : mRecords) {
+ mTileLayout.removeTile(record);
+ record.tile.removeCallback(record.callback);
+ }
+ }
+ mTileLayout = newLayout;
+ if (mHost != null) setTiles(mHost.getTiles());
+ newLayout.setListening(mListening);
+ if (needsDynamicRowsAndColumns()) {
+ newLayout.setMinRows(horizontal ? 2 : 1);
+ // Let's use 3 columns to match the current layout
+ newLayout.setMaxColumns(horizontal ? 3 : TileLayout.NO_MAX_COLUMNS);
+ }
+ updateTileLayoutMargins();
+ updateFooterMargin();
+ updateMediaHostContentMargins();
+ updateHorizontalLinearLayoutMargins();
+ updatePadding();
+ return true;
+ }
+ return false;
+ }
+
+ private void updateHorizontalLinearLayoutMargins() {
+ if (mHorizontalLinearLayout != null && !displayMediaMarginsOnMedia()) {
+ LayoutParams lp = (LayoutParams) mHorizontalLinearLayout.getLayoutParams();
+ lp.bottomMargin = mMediaTotalBottomMargin - getPaddingBottom();
+ mHorizontalLinearLayout.setLayoutParams(lp);
+ }
+ }
+
+ /**
+ * @return true if the margin bottom of the media view should be on the media host or false
+ * if they should be on the HorizontalLinearLayout. Returning {@code false} is useful
+ * to visually center the tiles in the Media view, which doesn't work when the
+ * expanded panel actually scrolls.
+ */
+ protected boolean displayMediaMarginsOnMedia() {
+ return true;
+ }
+
+ protected boolean needsDynamicRowsAndColumns() {
+ return true;
+ }
+
+ private void switchAllContentToParent(ViewGroup parent, QSTileLayout newLayout) {
+ int index = parent == this ? mMovableContentStartIndex : 0;
+
+ // Let's first move the tileLayout to the new parent, since that should come first.
+ switchToParent((View) newLayout, parent, index);
+ index++;
+
+ if (mSecurityFooter != null) {
+ View view = mSecurityFooter.getView();
+ LinearLayout.LayoutParams layoutParams = (LayoutParams) view.getLayoutParams();
+ if (mUsingHorizontalLayout && mHeaderContainer != null) {
+ // Adding the security view to the header, that enables us to avoid scrolling
+ layoutParams.width = 0;
+ layoutParams.weight = 1.6f;
+ switchToParent(view, mHeaderContainer, 1 /* always in second place */);
+ } else {
+ layoutParams.width = LayoutParams.WRAP_CONTENT;
+ layoutParams.weight = 0;
+ switchToParent(view, parent, index);
+ index++;
+ }
+ view.setLayoutParams(layoutParams);
+ }
+
+ if (mFooter != null) {
+ // Then the footer with the settings
+ switchToParent(mFooter, parent, index);
+ }
+ }
+
+ private void switchToParent(View child, ViewGroup parent, int index) {
+ ViewGroup currentParent = (ViewGroup) child.getParent();
+ if (currentParent != parent || currentParent.indexOfChild(child) != index) {
+ if (currentParent != null) {
+ currentParent.removeView(child);
+ }
+ parent.addView(child, index);
+ }
+ }
+
+ private boolean shouldUseHorizontalLayout() {
+ return mUsingMediaPlayer && mMediaHost.getVisible()
+ && getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_LANDSCAPE;
+ }
+
+ protected void reAttachMediaHost() {
+ if (!mUsingMediaPlayer) {
+ return;
+ }
+ boolean horizontal = shouldUseHorizontalLayout();
+ ViewGroup host = mMediaHost.getHostView();
+ ViewGroup newParent = horizontal ? mHorizontalLinearLayout : this;
+ ViewGroup currentParent = (ViewGroup) host.getParent();
+ if (currentParent != newParent) {
+ if (currentParent != null) {
+ currentParent.removeView(host);
+ }
+ newParent.addView(host);
+ LinearLayout.LayoutParams layoutParams = (LayoutParams) host.getLayoutParams();
+ layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
+ layoutParams.width = horizontal ? 0 : ViewGroup.LayoutParams.MATCH_PARENT;
+ layoutParams.weight = horizontal ? 1.2f : 0;
+ // Add any bottom margin, such that the total spacing is correct. This is only
+ // necessary if the view isn't horizontal, since otherwise the padding is
+ // carried in the parent of this view (to ensure correct vertical alignment)
+ layoutParams.bottomMargin = !horizontal || displayMediaMarginsOnMedia()
+ ? mMediaTotalBottomMargin - getPaddingBottom() : 0;
+ }
}
public void updateBrightnessMirror() {
@@ -457,13 +679,18 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
public void setListening(boolean listening, boolean expanded) {
setListening(listening && expanded);
- getFooter().setListening(listening);
+ if (mSecurityFooter != null) {
+ mSecurityFooter.setListening(listening);
+ }
// Set the listening as soon as the QS fragment starts listening regardless of the expansion,
// so it will update the current brightness before the slider is visible.
setBrightnessListening(listening);
}
public void setBrightnessListening(boolean listening) {
+ if (mBrightnessController == null) {
+ return;
+ }
if (listening) {
mBrightnessController.registerCallbacks();
} else {
@@ -472,11 +699,15 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
}
public void refreshAllTiles() {
- mBrightnessController.checkRestrictionAndSetEnabled();
+ if (mBrightnessController != null) {
+ mBrightnessController.checkRestrictionAndSetEnabled();
+ }
for (TileRecord r : mRecords) {
r.tile.refreshState();
}
- mFooter.refreshState();
+ if (mSecurityFooter != null) {
+ mSecurityFooter.refreshState();
+ }
}
public void showDetailAdapter(boolean show, DetailAdapter adapter, int[] locationInWindow) {
@@ -728,12 +959,15 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
return null;
}
- public QSSecurityFooter getFooter() {
- return mFooter;
+ @Nullable
+ public QSSecurityFooter getSecurityFooter() {
+ return mSecurityFooter;
}
public void showDeviceMonitoringDialog() {
- mFooter.showDeviceMonitoringDialog();
+ if (mSecurityFooter != null) {
+ mSecurityFooter.showDeviceMonitoringDialog();
+ }
}
public void setContentMargins(int startMargin, int endMargin) {
@@ -744,6 +978,24 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
updateTileLayoutMargins(mContentMarginStart - mVisualTilePadding,
mContentMarginEnd - mVisualTilePadding);
updateMediaHostContentMargins();
+ updateFooterMargin();
+ }
+
+ private void updateFooterMargin() {
+ if (mFooter != null) {
+ int footerMargin = 0;
+ int indicatorMargin = 0;
+ if (mUsingHorizontalLayout) {
+ footerMargin = mFooterMarginStartHorizontal;
+ indicatorMargin = footerMargin - mVisualMarginEnd;
+ }
+ updateMargins(mFooter, footerMargin, 0);
+ // The page indicator isn't centered anymore because of the visual positioning.
+ // Let's fix it by adding some margin
+ if (mFooterPageIndicator != null) {
+ updateMargins(mFooterPageIndicator, 0, indicatorMargin);
+ }
+ }
}
/**
@@ -754,16 +1006,30 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
* @param visualMarginEnd the visual end margin of the tile, adjusted for local insets
* to the tile. This can be set on a tileLayout
*/
- protected void updateTileLayoutMargins(int visualMarginStart, int visualMarginEnd) {
- updateMargins((View) mTileLayout, visualMarginStart, visualMarginEnd);
+ private void updateTileLayoutMargins(int visualMarginStart, int visualMarginEnd) {
+ mVisualMarginStart = visualMarginStart;
+ mVisualMarginEnd = visualMarginEnd;
+ updateTileLayoutMargins();
+ }
+
+ private void updateTileLayoutMargins() {
+ int marginEnd = mVisualMarginEnd;
+ if (mUsingHorizontalLayout) {
+ marginEnd = 0;
+ }
+ updateMargins((View) mTileLayout, mVisualMarginStart, marginEnd);
}
/**
* Update the margins of the media hosts
*/
protected void updateMediaHostContentMargins() {
- if (mUsingMediaPlayer && mMediaHost != null) {
- updateMargins(mMediaHost.getHostView(), mContentMarginStart, mContentMarginEnd);
+ if (mUsingMediaPlayer) {
+ int marginStart = mContentMarginStart;
+ if (mUsingHorizontalLayout) {
+ marginStart = 0;
+ }
+ updateMargins(mMediaHost.getHostView(), marginStart, mContentMarginEnd);
}
}
@@ -785,6 +1051,13 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
return mMediaHost;
}
+ /**
+ * Set the header container of quick settings.
+ */
+ public void setHeaderContainer(@NonNull ViewGroup headerContainer) {
+ mHeaderContainer = headerContainer;
+ }
+
private class H extends Handler {
private static final int SHOW_DETAIL = 1;
private static final int SET_TILE_VISIBILITY = 2;
@@ -812,6 +1085,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
}
}
+
protected static class Record {
DetailAdapter detailAdapter;
int x;
@@ -841,6 +1115,26 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
void setListening(boolean listening);
+ /**
+ * Set the minimum number of rows to show
+ *
+ * @param minRows the minimum.
+ */
+ default boolean setMinRows(int minRows) {
+ return false;
+ }
+
+ /**
+ * Set the max number of collums to show
+ *
+ * @param maxColumns the maximum
+ *
+ * @return true if the number of visible columns has changed.
+ */
+ default boolean setMaxColumns(int maxColumns) {
+ return false;
+ }
+
default void setExpansion(float expansion) {}
int getNumVisibleTiles();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index 476af20b78f4..7bcaa7263cc4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -51,6 +51,7 @@ import com.android.systemui.statusbar.policy.SecurityController;
public class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListener {
protected static final String TAG = "QSSecurityFooter";
protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final boolean DEBUG_FORCE_VISIBLE = false;
private final View mRootView;
private final TextView mFooterText;
@@ -60,7 +61,6 @@ public class QSSecurityFooter implements OnClickListener, DialogInterface.OnClic
private final SecurityController mSecurityController;
private final ActivityStarter mActivityStarter;
private final Handler mMainHandler;
- private final View mDivider;
private final UserManager mUm;
@@ -85,7 +85,6 @@ public class QSSecurityFooter implements OnClickListener, DialogInterface.OnClic
mActivityStarter = Dependency.get(ActivityStarter.class);
mSecurityController = Dependency.get(SecurityController.class);
mHandler = new H(Dependency.get(Dependency.BG_LOOPER));
- mDivider = qsPanel == null ? null : qsPanel.getDivider();
mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
}
@@ -177,7 +176,7 @@ public class QSSecurityFooter implements OnClickListener, DialogInterface.OnClic
boolean hasCACerts, boolean hasCACertsInWorkProfile, boolean isNetworkLoggingEnabled,
String vpnName, String vpnNameWorkProfile, CharSequence organizationName,
CharSequence workProfileName) {
- if (isDeviceManaged) {
+ if (isDeviceManaged || DEBUG_FORCE_VISIBLE) {
if (hasCACerts || hasCACertsInWorkProfile || isNetworkLoggingEnabled) {
if (organizationName == null) {
return mContext.getString(
@@ -451,8 +450,7 @@ public class QSSecurityFooter implements OnClickListener, DialogInterface.OnClic
if (mFooterTextContent != null) {
mFooterText.setText(mFooterTextContent);
}
- mRootView.setVisibility(mIsVisible ? View.VISIBLE : View.GONE);
- if (mDivider != null) mDivider.setVisibility(mIsVisible ? View.GONE : View.VISIBLE);
+ mRootView.setVisibility(mIsVisible || DEBUG_FORCE_VISIBLE ? View.VISIBLE : View.GONE);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 94b4cee92965..affb7b91b6a5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -24,7 +24,6 @@ import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
-import android.view.ViewGroup;
import android.widget.LinearLayout;
import com.android.internal.logging.UiEventLogger;
@@ -61,15 +60,7 @@ public class QuickQSPanel extends QSPanel {
private boolean mDisabledByPolicy;
private int mMaxTiles;
protected QSPanel mFullPanel;
- /** Whether or not the QuickQSPanel currently contains a media player. */
- private boolean mShowHorizontalTileLayout;
- private LinearLayout mHorizontalLinearLayout;
- // Only used with media
- private QSTileLayout mHorizontalTileLayout;
- private QSTileLayout mRegularTileLayout;
- private int mLastOrientation = -1;
- private int mMediaBottomMargin;
@Inject
public QuickQSPanel(
@@ -82,59 +73,8 @@ public class QuickQSPanel extends QSPanel {
UiEventLogger uiEventLogger
) {
super(context, attrs, dumpManager, broadcastDispatcher, qsLogger, mediaHost, uiEventLogger);
- if (mFooter != null) {
- removeView(mFooter.getView());
- }
- if (mTileLayout != null) {
- for (int i = 0; i < mRecords.size(); i++) {
- mTileLayout.removeTile(mRecords.get(i));
- }
- removeView((View) mTileLayout);
- }
- mMediaBottomMargin = getResources().getDimensionPixelSize(
- R.dimen.quick_settings_media_extra_bottom_margin);
- if (mUsingMediaPlayer) {
- mHorizontalLinearLayout = new LinearLayout(mContext);
- mHorizontalLinearLayout.setOrientation(LinearLayout.HORIZONTAL);
- mHorizontalLinearLayout.setClipChildren(false);
- mHorizontalLinearLayout.setClipToPadding(false);
-
- DoubleLineTileLayout horizontalTileLayout = new DoubleLineTileLayout(context,
- mUiEventLogger);
- horizontalTileLayout.setPaddingRelative(
- horizontalTileLayout.getPaddingStart(),
- horizontalTileLayout.getPaddingTop(),
- horizontalTileLayout.getPaddingEnd(),
- mContext.getResources().getDimensionPixelSize(
- R.dimen.qqs_horizonal_tile_padding_bottom));
- mHorizontalTileLayout = horizontalTileLayout;
- mRegularTileLayout = new HeaderTileLayout(context, mUiEventLogger);
- LayoutParams lp = new LayoutParams(0, LayoutParams.WRAP_CONTENT, 1);
- int marginSize = (int) mContext.getResources().getDimension(R.dimen.qqs_media_spacing);
- lp.setMarginStart(0);
- lp.setMarginEnd(marginSize);
- lp.gravity = Gravity.CENTER_VERTICAL;
- mHorizontalLinearLayout.addView((View) mHorizontalTileLayout, lp);
-
- sDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns);
-
- boolean useHorizontal = shouldUseHorizontalTileLayout();
- mTileLayout = useHorizontal ? mHorizontalTileLayout : mRegularTileLayout;
- mTileLayout.setListening(mListening);
- addView(mHorizontalLinearLayout, 0 /* Between brightness and footer */);
- ((View) mRegularTileLayout).setVisibility(!useHorizontal ? View.VISIBLE : View.GONE);
- mHorizontalLinearLayout.setVisibility(useHorizontal ? View.VISIBLE : View.GONE);
- addView((View) mRegularTileLayout, 0);
- super.setPadding(0, 0, 0, 0);
- applyBottomMargin((View) mRegularTileLayout);
- } else {
- sDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns);
- mTileLayout = new HeaderTileLayout(context, mUiEventLogger);
- mTileLayout.setListening(mListening);
- addView((View) mTileLayout, 0 /* Between brightness and footer */);
- super.setPadding(0, 0, 0, 0);
- applyBottomMargin((View) mTileLayout);
- }
+ sDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns);
+ applyBottomMargin((View) mRegularTileLayout);
}
private void applyBottomMargin(View view) {
@@ -144,57 +84,47 @@ public class QuickQSPanel extends QSPanel {
view.setLayoutParams(layoutParams);
}
- private void reAttachMediaHost() {
- if (mMediaHost == null) {
- return;
- }
- boolean horizontal = shouldUseHorizontalTileLayout();
- ViewGroup host = mMediaHost.getHostView();
- ViewGroup newParent = horizontal ? mHorizontalLinearLayout : this;
- ViewGroup currentParent = (ViewGroup) host.getParent();
- if (currentParent != newParent) {
- if (currentParent != null) {
- currentParent.removeView(host);
- }
- newParent.addView(host);
- LinearLayout.LayoutParams layoutParams = (LayoutParams) host.getLayoutParams();
- layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
- layoutParams.width = horizontal ? 0 : ViewGroup.LayoutParams.MATCH_PARENT;
- layoutParams.weight = horizontal ? 1.5f : 0;
- layoutParams.bottomMargin = mMediaBottomMargin;
- }
+ @Override
+ protected void addSecurityFooter() {
+ // No footer needed
+ }
+
+ @Override
+ protected void addViewsAboveTiles() {
+ // Nothing to add above the tiles
+ }
+
+ @Override
+ protected TileLayout createRegularTileLayout() {
+ return new QuickQSPanel.HeaderTileLayout(mContext, mUiEventLogger);
}
@Override
- protected void addMediaHostView() {
- mMediaHost.setVisibleChangedListener((visible) -> {
- switchTileLayout();
- return null;
- });
+ protected QSTileLayout createHorizontalTileLayout() {
+ return new DoubleLineTileLayout(mContext, mUiEventLogger);
+ }
+
+ @Override
+ protected void initMediaHostState() {
mMediaHost.setExpansion(0.0f);
mMediaHost.setShowsOnlyActiveMedia(true);
mMediaHost.init(MediaHierarchyManager.LOCATION_QQS);
- reAttachMediaHost();
- updateMediaHostContentMargins();
}
@Override
- protected void updateTileLayoutMargins(int visualMarginStart, int visualMarginEnd) {
- if (mUsingMediaPlayer) {
- updateMargins((View) mRegularTileLayout, visualMarginStart, visualMarginEnd);
- updateMargins((View) mHorizontalTileLayout, visualMarginStart, 0);
- } else {
- updateMargins((View) mTileLayout, visualMarginStart, visualMarginEnd);
- }
+ protected boolean needsDynamicRowsAndColumns() {
+ return false; // QQS always have the same layout
}
@Override
- protected void updatePadding() {
- // QS Panel is setting a top padding by default, which we don't need.
+ protected boolean displayMediaMarginsOnMedia() {
+ // Margins should be on the container to visually center the view
+ return false;
}
@Override
- protected void addDivider() {
+ protected void updatePadding() {
+ // QS Panel is setting a top padding by default, which we don't need.
}
@Override
@@ -237,60 +167,6 @@ public class QuickQSPanel extends QSPanel {
}
@Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- if (newConfig.orientation != mLastOrientation) {
- mLastOrientation = newConfig.orientation;
- switchTileLayout();
- }
- }
-
- boolean switchTileLayout() {
- if (!mUsingMediaPlayer) return false;
- mShowHorizontalTileLayout = shouldUseHorizontalTileLayout();
- if (mShowHorizontalTileLayout && mHorizontalLinearLayout.getVisibility() == View.GONE) {
- mHorizontalLinearLayout.setVisibility(View.VISIBLE);
- ((View) mRegularTileLayout).setVisibility(View.GONE);
- mTileLayout.setListening(false);
- for (TileRecord record : mRecords) {
- mTileLayout.removeTile(record);
- record.tile.removeCallback(record.callback);
- }
- mTileLayout = mHorizontalTileLayout;
- if (mHost != null) setTiles(mHost.getTiles());
- mTileLayout.setListening(mListening);
- reAttachMediaHost();
- return true;
- } else if (!mShowHorizontalTileLayout
- && mHorizontalLinearLayout.getVisibility() == View.VISIBLE) {
- mHorizontalLinearLayout.setVisibility(View.GONE);
- ((View) mRegularTileLayout).setVisibility(View.VISIBLE);
- mTileLayout.setListening(false);
- for (TileRecord record : mRecords) {
- mTileLayout.removeTile(record);
- record.tile.removeCallback(record.callback);
- }
- mTileLayout = mRegularTileLayout;
- if (mHost != null) setTiles(mHost.getTiles());
- mTileLayout.setListening(mListening);
- reAttachMediaHost();
- return true;
- }
- return false;
- }
-
- private boolean shouldUseHorizontalTileLayout() {
- return mMediaHost.getVisible()
- && getResources().getConfiguration().orientation
- == Configuration.ORIENTATION_LANDSCAPE;
- }
-
- /** Returns true if this panel currently uses a horizontal tile layout. */
- public boolean usesHorizontalLayout() {
- return mShowHorizontalTileLayout;
- }
-
- @Override
public void setHost(QSTileHost host, QSCustomizer customizer) {
super.setHost(host, customizer);
setTiles(mHost.getTiles());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 20e47b2f2fa9..311eda2f4ad8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -36,13 +36,13 @@ import android.service.notification.ZenModeConfig;
import android.text.format.DateUtils;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.MathUtils;
import android.util.Pair;
import android.view.ContextThemeWrapper;
import android.view.DisplayCutout;
import android.view.View;
import android.view.WindowInsets;
import android.widget.ImageView;
-import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
@@ -55,6 +55,7 @@ import androidx.lifecycle.LifecycleRegistry;
import com.android.settingslib.Utils;
import com.android.systemui.BatteryMeterView;
import com.android.systemui.DualToneHandler;
+import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.DarkIconDispatcher;
@@ -149,6 +150,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements
private int mWaterfallTopInset;
private int mCutOutPaddingLeft;
private int mCutOutPaddingRight;
+ private float mExpandedHeaderAlpha = 1.0f;
+ private float mKeyguardExpansionFraction;
@Inject
public QuickStatusBarHeader(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
@@ -356,7 +359,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements
private void updateHeaderTextContainerAlphaAnimator() {
mHeaderTextContainerAlphaAnimator = new TouchAnimator.Builder()
- .addFloat(mHeaderTextContainerView, "alpha", 0, 0, 1)
+ .addFloat(mHeaderTextContainerView, "alpha", 0, 0, mExpandedHeaderAlpha)
.build();
}
@@ -403,6 +406,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements
updateResources();
}
}
+ mKeyguardExpansionFraction = keyguardExpansionFraction;
}
public void disable(int state1, int state2, boolean animate) {
@@ -596,4 +600,22 @@ public class QuickStatusBarHeader extends RelativeLayout implements
}
updateClockPadding();
}
+
+ public void setExpandedScrollAmount(int scrollY) {
+ // The scrolling of the expanded qs has changed. Since the header text isn't part of it,
+ // but would overlap content, we're fading it out.
+ float newAlpha = 1.0f;
+ if (mHeaderTextContainerView.getHeight() > 0) {
+ newAlpha = MathUtils.map(0, mHeaderTextContainerView.getHeight() / 2.0f, 1.0f, 0.0f,
+ scrollY);
+ newAlpha = Interpolators.ALPHA_OUT.getInterpolation(newAlpha);
+ }
+ mHeaderTextContainerView.setScrollY(scrollY);
+ if (newAlpha != mExpandedHeaderAlpha) {
+ mExpandedHeaderAlpha = newAlpha;
+ mHeaderTextContainerView.setAlpha(MathUtils.lerp(0.0f, mExpandedHeaderAlpha,
+ mKeyguardExpansionFraction));
+ updateHeaderTextContainerAlphaAnimator();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 383c29d90a22..694492a33524 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -17,6 +17,7 @@ import java.util.ArrayList;
public class TileLayout extends ViewGroup implements QSTileLayout {
+ public static final int NO_MAX_COLUMNS = 100;
private static final float TILE_ASPECT = 1.2f;
private static final String TAG = "TileLayout";
@@ -36,6 +37,9 @@ public class TileLayout extends ViewGroup implements QSTileLayout {
// Prototyping with less rows
private final boolean mLessRows;
+ private int mMinRows = 1;
+ private int mMaxColumns = NO_MAX_COLUMNS;
+ private int mResourceColumns;
public TileLayout(Context context) {
this(context, null);
@@ -64,6 +68,22 @@ public class TileLayout extends ViewGroup implements QSTileLayout {
}
}
+ @Override
+ public boolean setMinRows(int minRows) {
+ if (mMinRows != minRows) {
+ mMinRows = minRows;
+ updateResources();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean setMaxColumns(int maxColumns) {
+ mMaxColumns = maxColumns;
+ return updateColumns();
+ }
+
public void addTile(TileRecord tile) {
mRecords.add(tile);
tile.tile.setListening(this, mListening);
@@ -91,21 +111,26 @@ public class TileLayout extends ViewGroup implements QSTileLayout {
public boolean updateResources() {
final Resources res = mContext.getResources();
- final int columns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns));
+ mResourceColumns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns));
mCellHeight = mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_height);
mCellMarginHorizontal = res.getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal);
mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical);
mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top);
mMaxAllowedRows = Math.max(1, getResources().getInteger(R.integer.quick_settings_max_rows));
- if (mLessRows) mMaxAllowedRows = Math.max(1, mMaxAllowedRows - 1);
- if (mColumns != columns) {
- mColumns = columns;
+ if (mLessRows) mMaxAllowedRows = Math.max(mMinRows, mMaxAllowedRows - 1);
+ if (updateColumns()) {
requestLayout();
return true;
}
return false;
}
+ private boolean updateColumns() {
+ int oldColumns = mColumns;
+ mColumns = Math.min(mResourceColumns, mMaxColumns);
+ return oldColumns != mColumns;
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// If called with AT_MOST, it will limit the number of rows. If called with UNSPECIFIED
@@ -142,18 +167,19 @@ public class TileLayout extends ViewGroup implements QSTileLayout {
* Determines the maximum number of rows that can be shown based on height. Clips at a minimum
* of 1 and a maximum of mMaxAllowedRows.
*
- * @param heightMeasureSpec Available height.
+ * @param allowedHeight The height this view has visually available
* @param tilesCount Upper limit on the number of tiles to show. to prevent empty rows.
*/
- public boolean updateMaxRows(int heightMeasureSpec, int tilesCount) {
- final int availableHeight = MeasureSpec.getSize(heightMeasureSpec) - mCellMarginTop
+ public boolean updateMaxRows(int allowedHeight, int tilesCount) {
+ final int availableHeight = allowedHeight - mCellMarginTop
+ // Add the cell margin in order to divide easily by the height + the margin below
+ mCellMarginVertical;
final int previousRows = mRows;
mRows = availableHeight / (mCellHeight + mCellMarginVertical);
- if (mRows >= mMaxAllowedRows) {
+ if (mRows < mMinRows) {
+ mRows = mMinRows;
+ } else if (mRows >= mMaxAllowedRows) {
mRows = mMaxAllowedRows;
- } else if (mRows <= 1) {
- mRows = 1;
}
if (mRows > (tilesCount + mColumns - 1) / mColumns) {
mRows = (tilesCount + mColumns - 1) / mColumns;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 8889510cde28..6622b9485b67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -104,7 +104,6 @@ import com.android.systemui.statusbar.notification.row.ActivatableNotificationVi
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
-import com.android.systemui.statusbar.notification.stack.MediaHeaderView;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
@@ -3069,7 +3068,7 @@ public class NotificationPanelViewController extends PanelViewController {
return new TouchHandler() {
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
- if (mBlockTouches || mQsFullyExpanded && mQs.onInterceptTouchEvent(event)) {
+ if (mBlockTouches || mQsFullyExpanded && mQs.disallowPanelTouches()) {
return false;
}
initDownStates(event);
@@ -3096,7 +3095,8 @@ public class NotificationPanelViewController extends PanelViewController {
@Override
public boolean onTouch(View v, MotionEvent event) {
- if (mBlockTouches || (mQs != null && mQs.isCustomizing())) {
+ if (mBlockTouches || (mQsFullyExpanded && mQs != null
+ && mQs.disallowPanelTouches())) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/NeverExactlyLinearLayout.kt b/packages/SystemUI/src/com/android/systemui/util/NeverExactlyLinearLayout.kt
new file mode 100644
index 000000000000..bab93475c8bf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/NeverExactlyLinearLayout.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2020 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.util
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.LinearLayout
+
+/**
+ * Basically a normal linear layout but doesn't grow its children with weight 1 even when its
+ * measured with exactly.
+ */
+class NeverExactlyLinearLayout @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0
+) : LinearLayout(context, attrs, defStyleAttr) {
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+
+ val (widthExactly, usedWidthSpec, width) = getNonExactlyMeasureSpec(widthMeasureSpec)
+ val (heightExactly, usedHeightSpec, height) = getNonExactlyMeasureSpec(heightMeasureSpec)
+
+ super.onMeasure(usedWidthSpec, usedHeightSpec)
+ if (widthExactly || heightExactly) {
+ val newWidth = if (widthExactly) width else measuredWidth
+ val newHeight = if (heightExactly) height else measuredHeight
+ setMeasuredDimension(newWidth, newHeight)
+ }
+ }
+
+ /**
+ * Obtain a measurespec that's not exactly
+ *
+ * @return a triple, where we return 1. if this was exactly, 2. the new measurespec, 3. the size
+ * of the measurespec
+ */
+ private fun getNonExactlyMeasureSpec(measureSpec: Int): Triple<Boolean, Int, Int> {
+ var newSpec = measureSpec
+ val isExactly = MeasureSpec.getMode(measureSpec) == MeasureSpec.EXACTLY
+ val size = MeasureSpec.getSize(measureSpec)
+ if (isExactly) {
+ newSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST)
+ }
+ return Triple(isExactly, newSpec, size)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
index 05b31c86559b..cbb0711f78f8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
@@ -51,6 +51,7 @@ import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.policy.SecurityController;
+import com.android.systemui.util.animation.UniqueObjectHostView;
import org.junit.Before;
import org.junit.Test;
@@ -108,12 +109,14 @@ public class QSPanelTest extends SysuiTestCase {
mDependency.injectMockDependency(SecurityController.class);
mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
mContext.addMockSystemService(Context.USER_SERVICE, mock(UserManager.class));
+ when(mMediaHost.getHostView()).thenReturn(new UniqueObjectHostView(getContext()));
mUiEventLogger = new UiEventLoggerFake();
mTestableLooper.runWithLooper(() -> {
mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
mQsPanel = new QSPanel(mContext, null, mDumpManager, mBroadcastDispatcher,
mQSLogger, mMediaHost, mUiEventLogger);
+ mQsPanel.onFinishInflate();
// Provides a parent with non-zero size for QSPanel
mParentView = new FrameLayout(mContext);
mParentView.addView(mQsPanel);