diff --git a/core/res/res/layout/notification_material_action_list.xml b/core/res/res/layout/notification_material_action_list.xml index 547d3cafb621a..4670dcabe2118 100644 --- a/core/res/res/layout/notification_material_action_list.xml +++ b/core/res/res/layout/notification_material_action_list.xml @@ -21,7 +21,7 @@ 56dp + + 56dp + 37.5dp diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index c64a93485b079..29818df192e44 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2584,6 +2584,8 @@ + + diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java index 34c9c0de44596..078b3c6bbdea6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java @@ -374,10 +374,42 @@ public class NotificationContentView extends FrameLayout { mContentHeight = Math.max(Math.min(contentHeight, getHeight()), getMinHeight());; mUnrestrictedContentHeight = Math.max(contentHeight, getMinHeight()); selectLayout(mAnimate /* animate */, false /* force */); + + int minHeightHint = getMinContentHeightHint(); + + NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType); + if (wrapper != null) { + wrapper.setContentHeight(mContentHeight, minHeightHint); + } + + wrapper = getVisibleWrapper(mTransformationStartVisibleType); + if (wrapper != null) { + wrapper.setContentHeight(mContentHeight, minHeightHint); + } + updateClipping(); invalidateOutline(); } + /** + * @return the minimum apparent height that the wrapper should allow for the purpose + * of aligning elements at the bottom edge. If this is larger than the content + * height, the notification is clipped instead of being further shrunk. + */ + private int getMinContentHeightHint() { + if (mIsChildInGroup && (mVisibleType == VISIBLE_TYPE_SINGLELINE + || mTransformationStartVisibleType == VISIBLE_TYPE_SINGLELINE)) { + return mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.notification_action_list_height); + } + if (mHeadsUpChild != null) { + return mHeadsUpChild.getHeight(); + } else { + return mContractedChild.getHeight() + mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.notification_action_list_height); + } + } + private void updateContentTransformation() { int visibleType = calculateVisibleType(); if (visibleType != mVisibleType) { @@ -493,11 +525,15 @@ public class NotificationContentView extends FrameLayout { } else { int visibleType = calculateVisibleType(); if (visibleType != mVisibleType || force) { - View visibleView = getViewForVisibleType(visibleType); - if (visibleView != null) { - visibleView.setVisibility(VISIBLE); - transferRemoteInputFocus(visibleType); - } + View visibleView = getViewForVisibleType(visibleType); + if (visibleView != null) { + visibleView.setVisibility(VISIBLE); + transferRemoteInputFocus(visibleType); + } + NotificationViewWrapper visibleWrapper = getVisibleWrapper(visibleType); + if (visibleWrapper != null) { + visibleWrapper.setContentHeight(mContentHeight, getMinContentHeightHint()); + } if (animate && ((visibleType == VISIBLE_TYPE_EXPANDED && mExpandedChild != null) || (visibleType == VISIBLE_TYPE_HEADSUP && mHeadsUpChild != null) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActionListTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActionListTransformState.java new file mode 100644 index 0000000000000..c0373bef0f5ae --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActionListTransformState.java @@ -0,0 +1,60 @@ +/* + * 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 + */ + +package com.android.systemui.statusbar.notification; + +import android.text.Layout; +import android.text.TextUtils; +import android.util.Pools; +import android.view.View; +import android.widget.TextView; + +/** + * A transform state of the action list +*/ +public class ActionListTransformState extends TransformState { + + private static Pools.SimplePool sInstancePool + = new Pools.SimplePool<>(40); + + @Override + protected boolean sameAs(TransformState otherState) { + return otherState instanceof ActionListTransformState; + } + + public static ActionListTransformState obtain() { + ActionListTransformState instance = sInstancePool.acquire(); + if (instance != null) { + return instance; + } + return new ActionListTransformState(); + } + + @Override + protected void resetTransformedView() { + // We need to keep the Y transformation, because this is used to keep the action list + // aligned at the bottom, unrelated to transforms. + float y = getTransformedView().getTranslationY(); + super.resetTransformedView(); + getTransformedView().setTranslationY(y); + } + + @Override + public void recycle() { + super.recycle(); + sInstancePool.release(this); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java index 889bd5cac7a54..7ca2df99d76eb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java @@ -41,6 +41,10 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp private ProgressBar mProgressBar; private TextView mTitle; private TextView mText; + private View mActionsContainer; + + private int mContentHeight; + private int mMinHeightHint; protected NotificationTemplateViewWrapper(Context ctx, View view, ExpandableNotificationRow row) { super(ctx, view, row); @@ -123,6 +127,7 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp // It's still a viewstub mProgressBar = null; } + mActionsContainer = mView.findViewById(com.android.internal.R.id.actions_container); } @Override @@ -225,4 +230,21 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp (int) (gSource * (1f - t) + gTarget * t), (int) (bSource * (1f - t) + bTarget * t)); } + + @Override + public void setContentHeight(int contentHeight, int minHeightHint) { + super.setContentHeight(contentHeight, minHeightHint); + + mContentHeight = contentHeight; + mMinHeightHint = minHeightHint; + updateActionOffset(); + } + + private void updateActionOffset() { + if (mActionsContainer != null) { + // We should never push the actions higher than they are in the headsup view. + int constrainedContentHeight = Math.max(mContentHeight, mMinHeightHint); + mActionsContainer.setTranslationY(constrainedContentHeight - mView.getHeight()); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java index 7a0df1f54c64e..22519e6e4de37 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java @@ -158,4 +158,7 @@ public abstract class NotificationViewWrapper implements TransformableView { public void setShowingLegacyBackground(boolean showing) { } + + public void setContentHeight(int contentHeight, int minHeightHint) { + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java index 8207215ec4a9a..4e643f0fcae19 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java @@ -374,6 +374,11 @@ public class TransformState { result.initFrom(view); return result; } + if (view.getId() == com.android.internal.R.id.actions_container) { + ActionListTransformState result = ActionListTransformState.obtain(); + result.initFrom(view); + return result; + } if (view instanceof NotificationHeaderView) { HeaderTransformState result = HeaderTransformState.obtain(); result.initFrom(view); @@ -467,7 +472,7 @@ public class TransformState { resetTransformedView(); } - private void resetTransformedView() { + protected void resetTransformedView() { mTransformedView.setTranslationX(0); mTransformedView.setTranslationY(0); mTransformedView.setScaleX(1.0f); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java index a855aed886945..095265a0f3298 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -272,7 +272,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene public boolean requestScrollTo() { findScrollContainer(); - mScrollContainer.scrollTo(mScrollContainerChild); + mScrollContainer.lockScrollTo(mScrollContainerChild); return true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index ffa246ae3fdc5..33b113aee81d2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -243,6 +243,7 @@ public class NotificationStackScrollLayout extends ViewGroup = new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { + updateForcedScroll(); updateChildren(); mChildrenUpdateRequested = false; getViewTreeObserver().removeOnPreDrawListener(this); @@ -334,6 +335,7 @@ public class NotificationStackScrollLayout extends ViewGroup private boolean mDrawBackgroundAsSrc; private boolean mFadedOut; private boolean mGroupExpandedForMeasure; + private View mForcedScroll; private float mBackgroundFadeAmount = 1.0f; private static final Property BACKGROUND_FADE = new FloatProperty("backgroundFade") { @@ -591,6 +593,23 @@ public class NotificationStackScrollLayout extends ViewGroup clampScrollPosition(); } + private void updateForcedScroll() { + if (mForcedScroll != null && (!mForcedScroll.hasFocus() + || !mForcedScroll.isAttachedToWindow())) { + mForcedScroll = null; + } + if (mForcedScroll != null) { + ExpandableView expandableView = (ExpandableView) mForcedScroll; + int positionInLinearLayout = getPositionInLinearLayout(expandableView); + int targetScroll = targetScrollForView(expandableView, positionInLinearLayout); + + targetScroll = Math.max(0, Math.min(targetScroll, getScrollRange())); + if (mOwnScrollY < targetScroll || positionInLinearLayout < mOwnScrollY) { + mOwnScrollY = targetScroll; + } + } + } + private void requestChildrenUpdate() { if (!mChildrenUpdateRequested) { getViewTreeObserver().addOnPreDrawListener(mChildrenUpdater); @@ -978,11 +997,19 @@ public class NotificationStackScrollLayout extends ViewGroup mScrollingEnabled = enable; } + @Override + public void lockScrollTo(View v) { + if (mForcedScroll == v) { + return; + } + mForcedScroll = v; + scrollTo(v); + } + + @Override public boolean scrollTo(View v) { ExpandableView expandableView = (ExpandableView) v; - int positionInLinearLayout = getPositionInLinearLayout(v); - int targetScroll = positionInLinearLayout + expandableView.getIntrinsicHeight() + - getImeInset() - getHeight() + getTopPadding(); + int targetScroll = targetScrollForView(expandableView, getPositionInLinearLayout(v)); if (mOwnScrollY < targetScroll) { mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScroll - mOwnScrollY); @@ -993,6 +1020,15 @@ public class NotificationStackScrollLayout extends ViewGroup return false; } + /** + * @return the scroll necessary to make the bottom edge of {@param v} align with the top of + * the IME. + */ + private int targetScrollForView(ExpandableView v, int positionInLinearLayout) { + return positionInLinearLayout + v.getIntrinsicHeight() + + getImeInset() - getHeight() + getTopPadding(); + } + @Override public WindowInsets onApplyWindowInsets(WindowInsets insets) { mBottomInset = insets.getSystemWindowInsetBottom(); @@ -1111,6 +1147,7 @@ public class NotificationStackScrollLayout extends ViewGroup if (ev.getY() < mQsContainer.getBottom()) { return false; } + mForcedScroll = null; initVelocityTrackerIfNotExists(); mVelocityTracker.addMovement(ev); @@ -2786,6 +2823,14 @@ public class NotificationStackScrollLayout extends ViewGroup } } + @Override + public void clearChildFocus(View child) { + super.clearChildFocus(child); + if (mForcedScroll == child) { + mForcedScroll = null; + } + } + @Override public void requestDisallowLongPress() { removeLongPressCallback(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ScrollContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ScrollContainer.java index 17b7871bbfe02..b9d12ce8f1510 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ScrollContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ScrollContainer.java @@ -35,6 +35,12 @@ public interface ScrollContainer { */ boolean scrollTo(View v); + /** + * Like {@link #scrollTo(View)}, but keeps the scroll locked until the user + * scrolls, or {@param v} loses focus or is detached. + */ + void lockScrollTo(View v); + /** * Request that the view does not dismiss for the current touch. */