Changed the landscape experience of quick settings
When introducing Media into quick settings, the landscape experience never really worked well. The music player would be cut off on the bottom and users could never reach their playback controls. Instead of the vertical layout, we're now rendering quick settings side by side with the player. Using some densities, this could still be too large, so we're also introducing a scrollview for the expanded quick settings. This also makes sure all the paddings and spacings are up to spec. This also fixes behavior where a user could still collapse the panel while the user switcher was showing. Bug: 154332040 Test: atest SystemUITests Change-Id: I3b8babc2cc6a91e19824b67f043aa5044dc370b9
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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" />
|
||||
|
||||
|
||||
@@ -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" />
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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" />
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
21
packages/SystemUI/res/values-sw600dp-port/dimens.xml
Normal file
21
packages/SystemUI/res/values-sw600dp-port/dimens.xml
Normal file
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
21
packages/SystemUI/res/values-w650dp-land/dimens.xml
Normal file
21
packages/SystemUI/res/values-w650dp-land/dimens.xml
Normal file
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
@@ -341,18 +351,55 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
|
||||
setClipBounds(mClippingRect);
|
||||
}
|
||||
|
||||
@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() {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
@@ -318,11 +330,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
||||
return mQSPanel.isShowingCustomize() || mQSDetail.isShowingDetail();
|
||||
}
|
||||
|
||||
@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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
mTileLayout = (QSTileLayout) LayoutInflater.from(mContext).inflate(
|
||||
R.layout.qs_paged_tile_layout, this, false);
|
||||
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();
|
||||
|
||||
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 void addMediaHostView() {
|
||||
|
||||
protected QSTileLayout createHorizontalTileLayout() {
|
||||
return createRegularTileLayout();
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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,48 +84,42 @@ 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 addMediaHostView() {
|
||||
mMediaHost.setVisibleChangedListener((visible) -> {
|
||||
switchTileLayout();
|
||||
return null;
|
||||
});
|
||||
protected void addViewsAboveTiles() {
|
||||
// Nothing to add above the tiles
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TileLayout createRegularTileLayout() {
|
||||
return new QuickQSPanel.HeaderTileLayout(mContext, mUiEventLogger);
|
||||
}
|
||||
|
||||
@Override
|
||||
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 boolean displayMediaMarginsOnMedia() {
|
||||
// Margins should be on the container to visually center the view
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -193,10 +127,6 @@ public class QuickQSPanel extends QSPanel {
|
||||
// QS Panel is setting a top padding by default, which we don't need.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addDivider() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
@@ -236,60 +166,6 @@ public class QuickQSPanel extends QSPanel {
|
||||
super.drawTile(r, state);
|
||||
}
|
||||
|
||||
@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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user