From 060902665960ecf9c758b8efde3ec0e040ffc466 Mon Sep 17 00:00:00 2001 From: Aran Ink Date: Tue, 3 Mar 2020 13:23:17 -0500 Subject: [PATCH] Scroll wallet and controls under power menu. Bug: 150694556 Test: Manual -- wallet and controls scroll beneath power menu, and clicking/swiping them works as expected. User can still dismiss the menu by tapping on any empty space and power menu controls are still functional in all three current layouts (column, grid, grid_v2) in portrait/landscape/seascape. Change-Id: I31de9661fdcd794c845c5e0040c490bf1520cb65 --- .../res/layout/global_actions_grid_v2.xml | 118 ++-- .../res/layout/global_actions_wrapped.xml | 38 -- .../android/systemui/HardwareUiLayout.java | 570 ------------------ .../globalactions/GlobalActionsDialog.java | 19 +- .../globalactions/MinHeightScrollView.java | 43 ++ 5 files changed, 113 insertions(+), 675 deletions(-) delete mode 100644 packages/SystemUI/res/layout/global_actions_wrapped.xml delete mode 100644 packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java create mode 100644 packages/SystemUI/src/com/android/systemui/globalactions/MinHeightScrollView.java diff --git a/packages/SystemUI/res/layout/global_actions_grid_v2.xml b/packages/SystemUI/res/layout/global_actions_grid_v2.xml index ff0c6a756ca26..92ae1b95264f6 100644 --- a/packages/SystemUI/res/layout/global_actions_grid_v2.xml +++ b/packages/SystemUI/res/layout/global_actions_grid_v2.xml @@ -1,71 +1,75 @@ - - - + - - + + + + + + > - - - - - - + android:layout_height="wrap_content" + android:orientation="vertical" + > + + - - - + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/layout/global_actions_wrapped.xml b/packages/SystemUI/res/layout/global_actions_wrapped.xml deleted file mode 100644 index d4410702a7d19..0000000000000 --- a/packages/SystemUI/res/layout/global_actions_wrapped.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - diff --git a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java deleted file mode 100644 index ad2e0024065f7..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java +++ /dev/null @@ -1,570 +0,0 @@ -/* - * 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. - */ - -package com.android.systemui; - -import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE; -import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE; -import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; -import android.content.Context; -import android.provider.Settings; -import android.util.AttributeSet; -import android.view.Gravity; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewOutlineProvider; -import android.view.ViewTreeObserver; -import android.widget.LinearLayout; - -import com.android.systemui.tuner.TunerService; -import com.android.systemui.tuner.TunerService.Tunable; -import com.android.systemui.util.leak.RotationUtils; - -/** - * Layout for placing two containers at a specific physical position on the device, relative to the - * device's hardware, regardless of screen rotation. - */ -public class HardwareUiLayout extends MultiListLayout implements Tunable { - - private static final String EDGE_BLEED = "sysui_hwui_edge_bleed"; - private static final String ROUNDED_DIVIDER = "sysui_hwui_rounded_divider"; - private final int[] mTmp2 = new int[2]; - private ViewGroup mList; - private ViewGroup mSeparatedView; - private int mOldHeight; - private boolean mAnimating; - private AnimatorSet mAnimation; - private View mDivision; - private HardwareBgDrawable mListBackground; - private HardwareBgDrawable mSeparatedViewBackground; - private Animator mAnimator; - private boolean mCollapse; - private int mEndPoint; - private boolean mEdgeBleed; - private boolean mRoundedDivider; - private boolean mRotatedBackground; - private boolean mSwapOrientation = true; - - public HardwareUiLayout(Context context, AttributeSet attrs) { - super(context, attrs); - // Manually re-initialize mRotation to portrait-mode, since this view must always - // be constructed in portrait mode and rotated into the correct initial position. - mRotation = ROTATION_NONE; - updateSettings(); - } - - @Override - protected ViewGroup getSeparatedView() { - return findViewById(com.android.systemui.R.id.separated_button); - } - - @Override - protected ViewGroup getListView() { - return findViewById(android.R.id.list); - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - updateSettings(); - Dependency.get(TunerService.class).addTunable(this, EDGE_BLEED, ROUNDED_DIVIDER); - getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsListener); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsListener); - Dependency.get(TunerService.class).removeTunable(this); - } - - @Override - public void onTuningChanged(String key, String newValue) { - updateSettings(); - } - - private void updateSettings() { - mEdgeBleed = Settings.Secure.getInt(getContext().getContentResolver(), - EDGE_BLEED, 0) != 0; - mRoundedDivider = Settings.Secure.getInt(getContext().getContentResolver(), - ROUNDED_DIVIDER, 0) != 0; - updateEdgeMargin(mEdgeBleed ? 0 : getEdgePadding()); - mListBackground = new HardwareBgDrawable(mRoundedDivider, !mEdgeBleed, getContext()); - mSeparatedViewBackground = new HardwareBgDrawable(mRoundedDivider, !mEdgeBleed, - getContext()); - if (mList != null) { - mList.setBackground(mListBackground); - mSeparatedView.setBackground(mSeparatedViewBackground); - requestLayout(); - } - } - - private void updateEdgeMargin(int edge) { - if (mList != null) { - MarginLayoutParams params = (MarginLayoutParams) mList.getLayoutParams(); - if (mRotation == ROTATION_LANDSCAPE) { - params.topMargin = edge; - } else if (mRotation == ROTATION_SEASCAPE) { - params.bottomMargin = edge; - } else { - params.rightMargin = edge; - } - mList.setLayoutParams(params); - } - - if (mSeparatedView != null) { - MarginLayoutParams params = (MarginLayoutParams) mSeparatedView.getLayoutParams(); - if (mRotation == ROTATION_LANDSCAPE) { - params.topMargin = edge; - } else if (mRotation == ROTATION_SEASCAPE) { - params.bottomMargin = edge; - } else { - params.rightMargin = edge; - } - mSeparatedView.setLayoutParams(params); - } - } - - private int getEdgePadding() { - return getContext().getResources().getDimensionPixelSize(R.dimen.edge_margin); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - if (mList == null) { - if (getChildCount() != 0) { - mList = getListView(); - mList.setBackground(mListBackground); - mSeparatedView = getSeparatedView(); - mSeparatedView.setBackground(mSeparatedViewBackground); - updateEdgeMargin(mEdgeBleed ? 0 : getEdgePadding()); - mOldHeight = mList.getMeasuredHeight(); - - // Must be called to initialize view rotation correctly. - // Requires LayoutParams, hence why this isn't called during the constructor. - updateRotation(); - } else { - return; - } - } - int newHeight = mList.getMeasuredHeight(); - if (newHeight != mOldHeight) { - animateChild(mOldHeight, newHeight); - } - - post(() -> updatePaddingAndGravityIfTooTall()); - post(() -> updatePosition()); - } - - public void setSwapOrientation(boolean swapOrientation) { - mSwapOrientation = swapOrientation; - } - - private void updateRotation() { - int rotation = RotationUtils.getRotation(getContext()); - if (rotation != mRotation) { - rotate(mRotation, rotation); - mRotation = rotation; - } - } - - /** - * Requires LayoutParams to be set to work correctly, and therefore must be run after after - * the HardwareUILayout has been added to the view hierarchy. - */ - protected void rotate(int from, int to) { - super.rotate(from, to); - if (from != ROTATION_NONE && to != ROTATION_NONE) { - // Rather than handling this confusing case, just do 2 rotations. - rotate(from, ROTATION_NONE); - rotate(ROTATION_NONE, to); - return; - } - if (from == ROTATION_LANDSCAPE || to == ROTATION_SEASCAPE) { - rotateRight(); - } else { - rotateLeft(); - } - if (mAdapter.hasSeparatedItems()) { - if (from == ROTATION_SEASCAPE || to == ROTATION_SEASCAPE) { - // Separated view has top margin, so seascape separated view need special rotation, - // not a full left or right rotation. - swapLeftAndTop(mSeparatedView); - } else if (from == ROTATION_LANDSCAPE) { - rotateRight(mSeparatedView); - } else { - rotateLeft(mSeparatedView); - } - } - if (to != ROTATION_NONE) { - if (mList instanceof LinearLayout) { - mRotatedBackground = true; - mListBackground.setRotatedBackground(true); - mSeparatedViewBackground.setRotatedBackground(true); - LinearLayout linearLayout = (LinearLayout) mList; - if (mSwapOrientation) { - linearLayout.setOrientation(LinearLayout.HORIZONTAL); - setOrientation(LinearLayout.HORIZONTAL); - } - swapDimens(mList); - swapDimens(mSeparatedView); - } - } else { - if (mList instanceof LinearLayout) { - mRotatedBackground = false; - mListBackground.setRotatedBackground(false); - mSeparatedViewBackground.setRotatedBackground(false); - LinearLayout linearLayout = (LinearLayout) mList; - if (mSwapOrientation) { - linearLayout.setOrientation(LinearLayout.VERTICAL); - setOrientation(LinearLayout.VERTICAL); - } - swapDimens(mList); - swapDimens(mSeparatedView); - } - } - } - - @Override - public void onUpdateList() { - super.onUpdateList(); - - for (int i = 0; i < mAdapter.getCount(); i++) { - ViewGroup parent; - boolean separated = mAdapter.shouldBeSeparated(i); - if (separated) { - parent = getSeparatedView(); - } else { - parent = getListView(); - } - View v = mAdapter.getView(i, null, parent); - parent.addView(v); - } - } - - private void rotateRight() { - rotateRight(this); - rotateRight(mList); - swapDimens(this); - - LayoutParams p = (LayoutParams) mList.getLayoutParams(); - p.gravity = rotateGravityRight(p.gravity); - mList.setLayoutParams(p); - - LayoutParams separatedViewLayoutParams = (LayoutParams) mSeparatedView.getLayoutParams(); - separatedViewLayoutParams.gravity = rotateGravityRight(separatedViewLayoutParams.gravity); - mSeparatedView.setLayoutParams(separatedViewLayoutParams); - - setGravity(rotateGravityRight(getGravity())); - } - - private void swapDimens(View v) { - ViewGroup.LayoutParams params = v.getLayoutParams(); - int h = params.width; - params.width = params.height; - params.height = h; - v.setLayoutParams(params); - } - - private int rotateGravityRight(int gravity) { - int retGravity = 0; - int layoutDirection = getLayoutDirection(); - final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection); - final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK; - - switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { - case Gravity.CENTER_HORIZONTAL: - retGravity |= Gravity.CENTER_VERTICAL; - break; - case Gravity.RIGHT: - retGravity |= Gravity.BOTTOM; - break; - case Gravity.LEFT: - default: - retGravity |= Gravity.TOP; - break; - } - - switch (verticalGravity) { - case Gravity.CENTER_VERTICAL: - retGravity |= Gravity.CENTER_HORIZONTAL; - break; - case Gravity.BOTTOM: - retGravity |= Gravity.LEFT; - break; - case Gravity.TOP: - default: - retGravity |= Gravity.RIGHT; - break; - } - return retGravity; - } - - private void rotateLeft() { - rotateLeft(this); - rotateLeft(mList); - swapDimens(this); - - LayoutParams p = (LayoutParams) mList.getLayoutParams(); - p.gravity = rotateGravityLeft(p.gravity); - mList.setLayoutParams(p); - - LayoutParams separatedViewLayoutParams = (LayoutParams) mSeparatedView.getLayoutParams(); - separatedViewLayoutParams.gravity = rotateGravityLeft(separatedViewLayoutParams.gravity); - mSeparatedView.setLayoutParams(separatedViewLayoutParams); - - setGravity(rotateGravityLeft(getGravity())); - } - - private int rotateGravityLeft(int gravity) { - if (gravity == -1) { - gravity = Gravity.TOP | Gravity.START; - } - int retGravity = 0; - int layoutDirection = getLayoutDirection(); - final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection); - final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK; - - switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { - case Gravity.CENTER_HORIZONTAL: - retGravity |= Gravity.CENTER_VERTICAL; - break; - case Gravity.RIGHT: - retGravity |= Gravity.TOP; - break; - case Gravity.LEFT: - default: - retGravity |= Gravity.BOTTOM; - break; - } - - switch (verticalGravity) { - case Gravity.CENTER_VERTICAL: - retGravity |= Gravity.CENTER_HORIZONTAL; - break; - case Gravity.BOTTOM: - retGravity |= Gravity.RIGHT; - break; - case Gravity.TOP: - default: - retGravity |= Gravity.LEFT; - break; - } - return retGravity; - } - - private void rotateLeft(View v) { - v.setPadding(v.getPaddingTop(), v.getPaddingRight(), v.getPaddingBottom(), - v.getPaddingLeft()); - MarginLayoutParams params = (MarginLayoutParams) v.getLayoutParams(); - params.setMargins(params.topMargin, params.rightMargin, params.bottomMargin, - params.leftMargin); - v.setLayoutParams(params); - } - - private void rotateRight(View v) { - v.setPadding(v.getPaddingBottom(), v.getPaddingLeft(), v.getPaddingTop(), - v.getPaddingRight()); - MarginLayoutParams params = (MarginLayoutParams) v.getLayoutParams(); - params.setMargins(params.bottomMargin, params.leftMargin, params.topMargin, - params.rightMargin); - v.setLayoutParams(params); - } - - private void swapLeftAndTop(View v) { - v.setPadding(v.getPaddingTop(), v.getPaddingLeft(), v.getPaddingBottom(), - v.getPaddingRight()); - MarginLayoutParams params = (MarginLayoutParams) v.getLayoutParams(); - params.setMargins(params.topMargin, params.leftMargin, params.bottomMargin, - params.rightMargin); - v.setLayoutParams(params); - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - - post(() -> updatePosition()); - - } - - private void animateChild(int oldHeight, int newHeight) { - if (true) return; - if (mAnimating) { - mAnimation.cancel(); - } - mAnimating = true; - mAnimation = new AnimatorSet(); - mAnimation.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mAnimating = false; - } - }); - int fromTop = mList.getTop(); - int fromBottom = mList.getBottom(); - int toTop = fromTop - ((newHeight - oldHeight) / 2); - int toBottom = fromBottom + ((newHeight - oldHeight) / 2); - ObjectAnimator top = ObjectAnimator.ofInt(mList, "top", fromTop, toTop); - top.addUpdateListener(animation -> mListBackground.invalidateSelf()); - mAnimation.playTogether(top, - ObjectAnimator.ofInt(mList, "bottom", fromBottom, toBottom)); - } - - public void setDivisionView(View v) { - mDivision = v; - if (mDivision != null) { - mDivision.addOnLayoutChangeListener( - (v1, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> - updatePosition()); - } - updatePosition(); - } - - private void updatePosition() { - if (mList == null) return; - // If got separated button, setRotatedBackground to false, - // all items won't get white background. - boolean separated = mAdapter.hasSeparatedItems(); - mListBackground.setRotatedBackground(separated); - mSeparatedViewBackground.setRotatedBackground(separated); - if (mDivision != null && mDivision.getVisibility() == VISIBLE) { - int index = mRotatedBackground ? 0 : 1; - mDivision.getLocationOnScreen(mTmp2); - float trans = mRotatedBackground ? mDivision.getTranslationX() - : mDivision.getTranslationY(); - int viewTop = (int) (mTmp2[index] + trans); - mList.getLocationOnScreen(mTmp2); - viewTop -= mTmp2[index]; - setCutPoint(viewTop); - } else { - setCutPoint(mList.getMeasuredHeight()); - } - } - - private void setCutPoint(int point) { - int curPoint = mListBackground.getCutPoint(); - if (curPoint == point) return; - if (getAlpha() == 0 || curPoint == 0) { - mListBackground.setCutPoint(point); - return; - } - if (mAnimator != null) { - if (mEndPoint == point) { - return; - } - mAnimator.cancel(); - } - mEndPoint = point; - mAnimator = ObjectAnimator.ofInt(mListBackground, "cutPoint", curPoint, point); - if (mCollapse) { - mAnimator.setStartDelay(300); - mCollapse = false; - } - mAnimator.start(); - } - - // If current power menu height larger then screen height, remove padding to break power menu - // alignment and set menu center vertical within the screen. - private void updatePaddingAndGravityIfTooTall() { - int defaultTopPadding; - int viewsTotalHeight; - int separatedViewTopMargin; - int screenHeight; - int totalHeight; - int targetGravity; - boolean separated = mAdapter.hasSeparatedItems(); - MarginLayoutParams params = (MarginLayoutParams) mSeparatedView.getLayoutParams(); - switch (RotationUtils.getRotation(getContext())) { - case RotationUtils.ROTATION_LANDSCAPE: - defaultTopPadding = getPaddingLeft(); - viewsTotalHeight = mList.getMeasuredWidth() + mSeparatedView.getMeasuredWidth(); - separatedViewTopMargin = separated ? params.leftMargin : 0; - screenHeight = getMeasuredWidth(); - targetGravity = Gravity.CENTER_HORIZONTAL|Gravity.TOP; - break; - case RotationUtils.ROTATION_SEASCAPE: - defaultTopPadding = getPaddingRight(); - viewsTotalHeight = mList.getMeasuredWidth() + mSeparatedView.getMeasuredWidth(); - separatedViewTopMargin = separated ? params.leftMargin : 0; - screenHeight = getMeasuredWidth(); - targetGravity = Gravity.CENTER_HORIZONTAL|Gravity.BOTTOM; - break; - default: // Portrait - defaultTopPadding = getPaddingTop(); - viewsTotalHeight = mList.getMeasuredHeight() + mSeparatedView.getMeasuredHeight(); - separatedViewTopMargin = separated ? params.topMargin : 0; - screenHeight = getMeasuredHeight(); - targetGravity = Gravity.CENTER_VERTICAL|Gravity.RIGHT; - break; - } - totalHeight = defaultTopPadding + viewsTotalHeight + separatedViewTopMargin; - if (totalHeight >= screenHeight) { - setPadding(0, 0, 0, 0); - setGravity(targetGravity); - } - } - - @Override - public ViewOutlineProvider getOutlineProvider() { - return super.getOutlineProvider(); - } - - public void setCollapse() { - mCollapse = true; - } - - private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsListener = inoutInfo -> { - if (mHasOutsideTouch || (mList == null)) { - inoutInfo.setTouchableInsets( - ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME); - return; - } - inoutInfo.setTouchableInsets( - ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT); - inoutInfo.contentInsets.set(mList.getLeft(), mList.getTop(), - 0, getBottom() - mList.getBottom()); - }; - - private float getAnimationDistance() { - return getContext().getResources().getDimension( - com.android.systemui.R.dimen.global_actions_panel_width) / 2; - } - - @Override - public float getAnimationOffsetX() { - if (RotationUtils.getRotation(mContext) == ROTATION_NONE) { - return getAnimationDistance(); - } - return 0; - } - - @Override - public float getAnimationOffsetY() { - switch (RotationUtils.getRotation(getContext())) { - case RotationUtils.ROTATION_LANDSCAPE: - return -getAnimationDistance(); - case RotationUtils.ROTATION_SEASCAPE: - return getAnimationDistance(); - default: // Portrait - return 0; - } - } -} \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 24f505d5a395f..786ad2c7d82ac 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -1574,7 +1574,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, private ControlsUiController mControlsUiController; private ViewGroup mControlsView; - private ViewGroup mContainerView; ActionsDialog(Context context, MyAdapter adapter, GlobalActionsPanelPlugin.PanelViewController plugin, BlurUtils blurUtils, @@ -1671,7 +1670,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mControlsView = findViewById(com.android.systemui.R.id.global_actions_controls); mGlobalActionsLayout = findViewById(com.android.systemui.R.id.global_actions_view); mGlobalActionsLayout.setOutsideTouchListener(view -> dismiss()); - ((View) mGlobalActionsLayout.getParent()).setOnClickListener(view -> dismiss()); mGlobalActionsLayout.setListViewAccessibilityDelegate(new View.AccessibilityDelegate() { @Override public boolean dispatchPopulateAccessibilityEvent( @@ -1684,6 +1682,15 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mGlobalActionsLayout.setRotationListener(this::onRotate); mGlobalActionsLayout.setAdapter(mAdapter); + View globalActionsParent = (View) mGlobalActionsLayout.getParent(); + globalActionsParent.setOnClickListener(v -> dismiss()); + + // add fall-through dismiss handling to root view + View rootView = findViewById(com.android.systemui.R.id.global_actions_grid_root); + if (rootView != null) { + rootView.setOnClickListener(v -> dismiss()); + } + if (shouldUsePanel()) { initializePanel(); } @@ -1692,14 +1699,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mScrimAlpha = ScrimController.BUSY_SCRIM_ALPHA; } getWindow().setBackgroundDrawable(mBackgroundDrawable); - - if (mControlsView != null) { - mContainerView = findViewById(com.android.systemui.R.id.global_actions_container); - mContainerView.setOnTouchListener((v, e) -> { - dismiss(); - return true; - }); - } } private void fixNavBarClipping() { diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/MinHeightScrollView.java b/packages/SystemUI/src/com/android/systemui/globalactions/MinHeightScrollView.java new file mode 100644 index 0000000000000..622fa658f1b0e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/globalactions/MinHeightScrollView.java @@ -0,0 +1,43 @@ +/* + * 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.globalactions; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.ScrollView; + +/** + * When measured, this view sets the minimum height of its first child to be equal to its own + * target height. + * + * This ensures fall-through click handlers can be placed on this view's child component. + */ +public class MinHeightScrollView extends ScrollView { + public MinHeightScrollView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + View firstChild = getChildAt(0); + if (firstChild != null) { + firstChild.setMinimumHeight(MeasureSpec.getSize(heightMeasureSpec)); + } + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } +}