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:
Selim Cinek
2020-06-13 02:01:08 +00:00
committed by Android (Google) Code Review
36 changed files with 838 additions and 441 deletions

View File

@@ -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)

View File

@@ -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" />

View File

@@ -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" />

View File

@@ -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>

View File

@@ -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" />

View File

@@ -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" />

View File

@@ -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>

View File

@@ -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">

View File

@@ -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

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View 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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View 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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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())
}
}
}

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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() {

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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();

View File

@@ -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();

View File

@@ -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);
}
};

View File

@@ -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);

View File

@@ -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();
}
}
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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)
}
}

View File

@@ -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);