diff options
8 files changed, 238 insertions, 6 deletions
diff --git a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml index f699fce27043..7df6bc695555 100644 --- a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml +++ b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml @@ -22,6 +22,7 @@ android:focusable="true" android:clickable="true" > + <com.android.systemui.statusbar.NotificationBackgroundView android:id="@+id/backgroundNormal" android:layout_width="match_parent" android:layout_height="match_parent" @@ -65,4 +66,9 @@ /> </com.android.keyguard.AlphaOptimizedLinearLayout> + <com.android.systemui.statusbar.notification.FakeShadowView + android:id="@+id/fake_shadow" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + </com.android.systemui.statusbar.NotificationOverflowContainer> diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml index c4c45bb0be22..e456984c5f36 100644 --- a/packages/SystemUI/res/layout/status_bar_notification_row.xml +++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml @@ -76,4 +76,9 @@ android:layout_height="wrap_content" /> + <com.android.systemui.statusbar.notification.FakeShadowView + android:id="@+id/fake_shadow" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + </com.android.systemui.statusbar.ExpandableNotificationRow> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java index 315c5092922c..effe581ac9ca 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java @@ -35,6 +35,7 @@ import android.view.animation.PathInterpolator; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.classifier.FalsingManager; +import com.android.systemui.statusbar.notification.FakeShadowView; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; /** @@ -155,6 +156,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } }; private float mShadowAlpha = 1.0f; + private FakeShadowView mFakeShadow; public ActivatableNotificationView(Context context, AttributeSet attrs) { super(context, attrs); @@ -180,6 +182,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView protected void onFinishInflate() { super.onFinishInflate(); mBackgroundNormal = (NotificationBackgroundView) findViewById(R.id.backgroundNormal); + mFakeShadow = (FakeShadowView) findViewById(R.id.fake_shadow); mBackgroundDimmed = (NotificationBackgroundView) findViewById(R.id.backgroundDimmed); mBackgroundNormal.setCustomBackground(R.drawable.notification_material_bg); mBackgroundDimmed.setCustomBackground(R.drawable.notification_material_bg_dim); @@ -852,6 +855,14 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } } + @Override + public void setFakeShadowIntensity(float shadowIntensity, float outlineAlpha, int shadowYEnd, + int outlineTranslation) { + mFakeShadow.setFakeShadowTranslationZ(shadowIntensity * (getTranslationZ() + + FakeShadowView.SHADOW_SIBLING_TRESHOLD), outlineAlpha, shadowYEnd, + outlineTranslation); + } + public interface OnActivatedListener { void onActivated(ActivatableNotificationView view); void onActivationReset(ActivatableNotificationView view); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java index 782a38cc4023..f98e87d4848c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java @@ -70,6 +70,11 @@ public abstract class ExpandableOutlineView extends ExpandableView { } } + @Override + public float getOutlineAlpha() { + return mOutlineAlpha; + } + protected void setOutlineRect(RectF rect) { if (rect != null) { setOutlineRect(rect.left, rect.top, rect.right, rect.bottom); @@ -80,6 +85,11 @@ public abstract class ExpandableOutlineView extends ExpandableView { } } + @Override + public int getOutlineTranslation() { + return mCustomOutline ? mOutlineRect.left : 0; + } + protected void setOutlineRect(float left, float top, float right, float bottom) { setOutlineRect(true, left, top, right, bottom); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java index 3176e2e1851a..c9e1cff9c865 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java @@ -399,6 +399,18 @@ public abstract class ExpandableView extends FrameLayout { return false; } + public void setFakeShadowIntensity(float shadowIntensity, float outlineAlpha, int shadowYEnd, + int outlineTranslation) { + } + + public float getOutlineAlpha() { + return 0.0f; + } + + public int getOutlineTranslation() { + return 0; + } + /** * A listener notifying when {@link #getActualHeight} changes. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/FakeShadowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/FakeShadowView.java new file mode 100644 index 000000000000..32c26ba8ba13 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/FakeShadowView.java @@ -0,0 +1,84 @@ +/* + * 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.annotation.Nullable; +import android.content.Context; +import android.graphics.Outline; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewOutlineProvider; +import android.widget.LinearLayout; + +import com.android.systemui.statusbar.AlphaOptimizedFrameLayout; + +/** + * A view used to cast a shadow of a certain size on another view + */ +public class FakeShadowView extends AlphaOptimizedFrameLayout { + public static final float SHADOW_SIBLING_TRESHOLD = 0.1f; + + private View mFakeShadow; + private float mOutlineAlpha; + + public FakeShadowView(Context context) { + this(context, null); + } + + public FakeShadowView(Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public FakeShadowView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public FakeShadowView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + mFakeShadow = new View(context); + mFakeShadow.setVisibility(INVISIBLE); + mFakeShadow.setLayoutParams(new LinearLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + (int) (48 * getResources().getDisplayMetrics().density))); + mFakeShadow.setOutlineProvider(new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setRect(0, 0, getWidth(), mFakeShadow.getHeight()); + outline.setAlpha(mOutlineAlpha); + } + }); + addView(mFakeShadow); + } + + public void setFakeShadowTranslationZ(float fakeShadowTranslationZ, float outlineAlpha, + int shadowYEnd, int outlineTranslation) { + if (fakeShadowTranslationZ == 0.0f) { + mFakeShadow.setVisibility(INVISIBLE); + } else { + mFakeShadow.setVisibility(VISIBLE); + mFakeShadow.setTranslationZ(fakeShadowTranslationZ); + mFakeShadow.setTranslationX(outlineTranslation); + mFakeShadow.setTranslationY(shadowYEnd - mFakeShadow.getHeight()); + if (outlineAlpha != mOutlineAlpha) { + mOutlineAlpha = outlineAlpha; + mFakeShadow.invalidateOutline(); + } + } + } +} 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 8f9a2243bad3..fe06c3affa12 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -63,6 +63,7 @@ import com.android.systemui.statusbar.NotificationSettingsIconRow; import com.android.systemui.statusbar.NotificationSettingsIconRow.SettingsIconRowListener; import com.android.systemui.statusbar.StackScrollerDecorView; import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.notification.FakeShadowView; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.PhoneStatusBar; import com.android.systemui.statusbar.phone.ScrimController; @@ -70,6 +71,8 @@ import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.ScrollAdapter; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.HashSet; /** @@ -93,7 +96,7 @@ public class NotificationStackScrollLayout extends ViewGroup private static final int INVALID_POINTER = -1; private ExpandHelper mExpandHelper; - private SwipeHelper mSwipeHelper; + private NotificationSwipeHelper mSwipeHelper; private boolean mSwipingInProgress; private int mCurrentStackHeight = Integer.MAX_VALUE; private final Paint mBackgroundPaint = new Paint(); @@ -277,6 +280,7 @@ public class NotificationStackScrollLayout extends ViewGroup private int mBgColor; private float mDimAmount; private ValueAnimator mDimAnimator; + private ArrayList<ExpandableView> mTmpSortedChildren = new ArrayList<>(); private Animator.AnimatorListener mDimEndListener = new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -292,6 +296,31 @@ public class NotificationStackScrollLayout extends ViewGroup } }; private ViewGroup mQsContainer; + private boolean mContinuousShadowUpdate; + private ViewTreeObserver.OnPreDrawListener mShadowUpdater + = new ViewTreeObserver.OnPreDrawListener() { + + @Override + public boolean onPreDraw() { + updateViewShadows(); + return true; + } + }; + private Comparator<ExpandableView> mViewPositionComparator = new Comparator<ExpandableView>() { + @Override + public int compare(ExpandableView view, ExpandableView otherView) { + float endY = view.getTranslationY() + view.getActualHeight(); + float otherEndY = otherView.getTranslationY() + otherView.getActualHeight(); + if (endY < otherEndY) { + return -1; + } else if (endY > otherEndY) { + return 1; + } else { + // The two notifications end at the same location + return 0; + } + } + }; public NotificationStackScrollLayout(Context context) { this(context, null); @@ -667,6 +696,7 @@ public class NotificationStackScrollLayout extends ViewGroup } mSwipedOutViews.add(v); mAmbientState.onDragFinished(v); + updateContinuousShadowDrawing(); if (v instanceof ExpandableNotificationRow) { ExpandableNotificationRow row = (ExpandableNotificationRow) v; if (row.isHeadsUp()) { @@ -689,6 +719,7 @@ public class NotificationStackScrollLayout extends ViewGroup @Override public void onChildSnappedBack(View animView, float targetLeft) { mAmbientState.onDragFinished(animView); + updateContinuousShadowDrawing(); if (!mDragAnimPendingChildren.contains(animView)) { if (mAnimationsEnabled) { mSnappedBackChildren.add(animView); @@ -721,6 +752,7 @@ public class NotificationStackScrollLayout extends ViewGroup mFalsingManager.onNotificatonStartDismissing(); setSwipingInProgress(true); mAmbientState.onBeginDrag(v); + updateContinuousShadowDrawing(); if (mAnimationsEnabled && (mIsExpanded || !isPinnedHeadsUp(v))) { mDragAnimPendingChildren.add(v); mNeedsAnimation = true; @@ -2242,6 +2274,7 @@ public class NotificationStackScrollLayout extends ViewGroup setAnimationRunning(true); mAnimationEvents.clear(); updateBackground(); + updateViewShadows(); } else { applyCurrentState(); } @@ -2824,6 +2857,43 @@ public class NotificationStackScrollLayout extends ViewGroup } runAnimationFinishedRunnables(); updateBackground(); + updateViewShadows(); + } + + private void updateViewShadows() { + // we need to work around an issue where the shadow would not cast between siblings when + // their z difference is between 0 and 0.1 + + // Lefts first sort by Z difference + for (int i = 0; i < getChildCount(); i++) { + ExpandableView child = (ExpandableView) getChildAt(i); + if (child.getVisibility() != GONE) { + mTmpSortedChildren.add(child); + } + } + Collections.sort(mTmpSortedChildren, mViewPositionComparator); + + // Now lets update the shadow for the views + ExpandableView previous = null; + for (int i = 0; i < mTmpSortedChildren.size(); i++) { + ExpandableView expandableView = mTmpSortedChildren.get(i); + float translationZ = expandableView.getTranslationZ(); + float otherZ = previous == null ? translationZ : previous.getTranslationZ(); + float diff = otherZ - translationZ; + if (diff <= 0.0f || diff >= FakeShadowView.SHADOW_SIBLING_TRESHOLD) { + // There is no fake shadow to be drawn + expandableView.setFakeShadowIntensity(0.0f, 0.0f, 0, 0); + } else { + float yLocation = previous.getTranslationY() + previous.getActualHeight() - + expandableView.getTranslationY(); + expandableView.setFakeShadowIntensity(diff / FakeShadowView.SHADOW_SIBLING_TRESHOLD, + previous.getOutlineAlpha(), (int) yLocation, + previous.getOutlineTranslation()); + } + previous = expandableView; + } + + mTmpSortedChildren.clear(); } public void goToFullShade(long delay) { @@ -3262,6 +3332,7 @@ public class NotificationStackScrollLayout extends ViewGroup getViewTreeObserver().removeOnPreDrawListener(mBackgroundUpdater); } mAnimationRunning = animationRunning; + updateContinuousShadowDrawing(); } } @@ -3549,6 +3620,18 @@ public class NotificationStackScrollLayout extends ViewGroup } } + private void updateContinuousShadowDrawing() { + boolean continuousShadowUpdate = mAnimationRunning + || !mAmbientState.getDraggedViews().isEmpty(); + if (continuousShadowUpdate != mContinuousShadowUpdate) { + if (continuousShadowUpdate) { + getViewTreeObserver().addOnPreDrawListener(mShadowUpdater); + } else { + getViewTreeObserver().removeOnPreDrawListener(mShadowUpdater); + } + } + } + static class AnimationEvent { static AnimationFilter[] FILTERS = new AnimationFilter[] { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java index e87b36397c7a..d78d62680860 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java @@ -24,6 +24,7 @@ import android.view.ViewGroup; import com.android.systemui.R; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.ExpandableView; +import com.android.systemui.statusbar.notification.FakeShadowView; import java.util.ArrayList; import java.util.HashSet; @@ -605,20 +606,40 @@ public class StackScrollAlgorithm { private void updateZValuesForState(StackScrollState resultState, StackScrollAlgorithmState algorithmState, AmbientState ambientState) { int childCount = algorithmState.visibleChildren.size(); - int childrenOnTop = 0; + float childrenOnTop = 0.0f; for (int i = childCount - 1; i >= 0; i--) { ExpandableView child = algorithmState.visibleChildren.get(i); StackViewState childViewState = resultState.getViewStateForView(child); if (i > (childCount - 1 - algorithmState.itemsInBottomStack)) { // We are in the bottom stack float numItemsAbove = i - (childCount - 1 - algorithmState.itemsInBottomStack); - childViewState.zTranslation = mZBasicHeight - - numItemsAbove * mZDistanceBetweenElements; + float zSubtraction; + if (numItemsAbove <= 1.0f) { + float factor = 0.2f; + // Lets fade in slower to the threshold to make the shadow fade in look nicer + if (numItemsAbove <= factor) { + zSubtraction = FakeShadowView.SHADOW_SIBLING_TRESHOLD + * numItemsAbove * (1.0f / factor); + } else { + zSubtraction = FakeShadowView.SHADOW_SIBLING_TRESHOLD + + (numItemsAbove - factor) * (1.0f / (1.0f - factor)) + * (mZDistanceBetweenElements + - FakeShadowView.SHADOW_SIBLING_TRESHOLD); + } + } else { + zSubtraction = numItemsAbove * mZDistanceBetweenElements; + } + childViewState.zTranslation = mZBasicHeight - zSubtraction; } else if (child.mustStayOnScreen() && childViewState.yTranslation < ambientState.getTopPadding() + ambientState.getStackTranslation()) { - // TODO; do this more cleanly - childrenOnTop++; + if (childrenOnTop != 0.0f) { + childrenOnTop++; + } else { + float overlap = ambientState.getTopPadding() + + ambientState.getStackTranslation() - childViewState.yTranslation; + childrenOnTop += Math.min(1.0f, overlap / childViewState.height); + } childViewState.zTranslation = mZBasicHeight + childrenOnTop * mZDistanceBetweenElements; } else { |