Merge changes I3b8babc2,I56ea3eee into rvc-dev
* changes: Changed the landscape experience of quick settings Fixed an issue where the media player would shift when editing QS
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>
|
||||
@@ -1264,8 +1263,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,31 +432,63 @@ 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();
|
||||
pinToBottom(absoluteBottomPosition, mQSPanel.getMediaHost());
|
||||
pinToBottom(absoluteBottomPosition - mHeader.getPaddingBottom(),
|
||||
mHeader.getHeaderQsPanel().getMediaHost());
|
||||
// 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(expandedMediaPosition, mQSPanel.getMediaHost(), true /* expanded */);
|
||||
// The expanded media host should never move above the laid out position
|
||||
pinToBottom(absoluteBottomPosition, mHeader.getHeaderQsPanel().getMediaHost(),
|
||||
false /* expanded */);
|
||||
}
|
||||
}
|
||||
|
||||
private void pinToBottom(float absoluteBottomPosition, MediaHost mediaHost) {
|
||||
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();
|
||||
hostView.setTranslationY(targetPosition - currentPosition);
|
||||
float translationY = targetPosition - currentPosition;
|
||||
if (expanded) {
|
||||
// Never go below the laid out position. This is necessary since the qs panel can
|
||||
// change in height and we don't want to ever go below it's position
|
||||
translationY = Math.min(translationY, 0);
|
||||
} else {
|
||||
translationY = Math.max(translationY, 0);
|
||||
}
|
||||
hostView.setTranslationY(translationY);
|
||||
} else {
|
||||
hostView.setTranslationY(0);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
@@ -504,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.
|
||||
@@ -521,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;
|
||||
|
||||
@@ -3071,7 +3071,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);
|
||||
@@ -3098,7 +3098,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