package com.android.systemui.statusbar.stack;
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator
.ExpandAnimationParameters;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.TimeAnimator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.FloatRange;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.service.notification.StatusBarNotification;
import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
import android.support.v4.graphics.ColorUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.MathUtils;
import android.util.Pair;
import android.view.ContextThemeWrapper;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowInsets;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.OverScroller;
import android.widget.ScrollView;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.keyguard.KeyguardSliceView;
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.ExpandHelper;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.SwipeHelper;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.ActivatableNotificationView;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.FooterView;
import com.android.systemui.statusbar.NotificationBlockingHelperManager;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.NotificationGuts;
import com.android.systemui.statusbar.NotificationListContainer;
import com.android.systemui.statusbar.NotificationLogger;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.NotificationSnooze;
import com.android.systemui.statusbar.StackScrollerDecorView;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.FakeShadowView;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.ScrollAdapter;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.function.BiConsumer;
public class NotificationStackScrollLayout extends ViewGroup
implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter,
ExpandableView.OnHeightChangedListener, NotificationGroupManager.OnGroupChangeListener,
NotificationMenuRowPlugin.OnMenuEventListener, VisibilityLocationProvider,
NotificationListContainer {
public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
private static final String TAG = "StackScroller";
private static final boolean DEBUG = false;
private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f;
private static final float RUBBER_BAND_FACTOR_AFTER_EXPAND = 0.15f;
private static final float RUBBER_BAND_FACTOR_ON_PANEL_EXPAND = 0.21f;
private static final int INVALID_POINTER = -1;
private ExpandHelper mExpandHelper;
private NotificationSwipeHelper mSwipeHelper;
private boolean mSwipingInProgress;
private int mCurrentStackHeight = Integer.MAX_VALUE;
private final Paint mBackgroundPaint = new Paint();
private final boolean mShouldDrawNotificationBackground;
private float mExpandedHeight;
private int mOwnScrollY;
private int mMaxLayoutHeight;
private VelocityTracker mVelocityTracker;
private OverScroller mScroller;
private Runnable mFinishScrollingCallback;
private int mTouchSlop;
private int mMinimumVelocity;
private int mMaximumVelocity;
private int mOverflingDistance;
private float mMaxOverScroll;
private boolean mIsBeingDragged;
private int mLastMotionY;
private int mDownX;
private int mActivePointerId = INVALID_POINTER;
private boolean mTouchIsClick;
private float mInitialTouchX;
private float mInitialTouchY;
private Paint mDebugPaint;
private int mContentHeight;
private int mIntrinsicContentHeight;
private int mCollapsedSize;
private int mPaddingBetweenElements;
private int mIncreasedPaddingBetweenElements;
private int mMaxTopPadding;
private int mRegularTopPadding;
private int mDarkTopPadding;
private int mTopPadding;
private int mDarkSeparatorPadding;
private int mBottomMargin;
private int mBottomInset = 0;
private float mQsExpansionFraction;
protected final StackScrollAlgorithm mStackScrollAlgorithm;
private StackScrollState mCurrentStackScrollState = new StackScrollState(this);
private final AmbientState mAmbientState;
private NotificationGroupManager mGroupManager;
private HashSet<View> mChildrenToAddAnimated = new HashSet<>();
private ArrayList<View> mAddedHeadsUpChildren = new ArrayList<>();
private ArrayList<View> mChildrenToRemoveAnimated = new ArrayList<>();
private ArrayList<View> mSnappedBackChildren = new ArrayList<>();
private ArrayList<View> mDragAnimPendingChildren = new ArrayList<>();
private ArrayList<View> mChildrenChangingPositions = new ArrayList<>();
private HashSet<View> mFromMoreCardAdditions = new HashSet<>();
private ArrayList<AnimationEvent> mAnimationEvents = new ArrayList<>();
private ArrayList<View> mSwipedOutViews = new ArrayList<>();
private final StackStateAnimator mStateAnimator = new StackStateAnimator(this);
private boolean mAnimationsEnabled;
private boolean mChangePositionInProgress;
private boolean mChildTransferInProgress;
private float mOverScrolledTopPixels;
private float mOverScrolledBottomPixels;
private NotificationLogger.OnChildLocationsChangedListener mListener;
private OnOverscrollTopChangedListener mOverscrollTopChangedListener;
private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
private OnEmptySpaceClickListener mOnEmptySpaceClickListener;
private boolean mNeedsAnimation;
private boolean mTopPaddingNeedsAnimation;
private boolean mDimmedNeedsAnimation;
private boolean mHideSensitiveNeedsAnimation;
private boolean mDarkNeedsAnimation;
private int mDarkAnimationOriginIndex;
private boolean mActivateNeedsAnimation;
private boolean mGoToFullShadeNeedsAnimation;
private boolean mIsExpanded = true;
private boolean mChildrenUpdateRequested;
private boolean mIsExpansionChanging;
private boolean mPanelTracking;
private boolean mExpandingNotification;
private boolean mExpandedInThisMotion;
private boolean mShouldShowShelfOnly;
protected boolean mScrollingEnabled;
protected FooterView mFooterView;
protected EmptyShadeView mEmptyShadeView;
private boolean mDismissAllInProgress;
private boolean mFadeNotificationsOnDismiss;
private boolean mScrolledToTopOnFirstDown;
private float mMinTopOverScrollToEscape;
private int mIntrinsicPadding;
private float mStackTranslation;
private float mTopPaddingOverflow;
private boolean mDontReportNextOverScroll;
private boolean mDontClampNextScroll;
private boolean mNeedViewResizeAnimation;
private View mExpandedGroupView;
private boolean mEverythingNeedsAnimation;
private int mMaxScrollAfterExpand;
private ExpandableNotificationRow.LongPressListener mLongPressListener;
private NotificationMenuRowPlugin mCurrMenuRow;
private View mTranslatingParentView;
private View mMenuExposedView;
boolean mCheckForLeavebehind;
private boolean mOnlyScrollingInThisMotion;
private boolean mDisallowDismissInThisMotion;
private boolean mDisallowScrollingInThisMotion;
private long mGoToFullShadeDelay;
private ViewTreeObserver.OnPreDrawListener mChildrenUpdater
= new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
updateForcedScroll();
updateChildren();
mChildrenUpdateRequested = false;
getViewTreeObserver().removeOnPreDrawListener(this);
return true;
}
};
private StatusBar mStatusBar;
private int[] mTempInt2 = new int[2];
private boolean mGenerateChildOrderChangedEvent;
private HashSet<Runnable> mAnimationFinishedRunnables = new HashSet<>();
private HashSet<ExpandableView> mClearTransientViewsWhenFinished = new HashSet<>();
private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
= new HashSet<>();
private HeadsUpManagerPhone mHeadsUpManager;
private NotificationRoundnessManager mRoundnessManager = new NotificationRoundnessManager();
private boolean mTrackingHeadsUp;
private ScrimController mScrimController;
private boolean mForceNoOverlappingRendering;
private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>();
private FalsingManager mFalsingManager;
private boolean mAnimationRunning;
private ViewTreeObserver.OnPreDrawListener mRunningAnimationUpdater
= new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
onPreDrawDuringAnimation();
return true;
}
};
private Rect mBackgroundBounds = new Rect();
private Rect mStartAnimationRect = new Rect();
private Rect mEndAnimationRect = new Rect();
private Rect mCurrentBounds = new Rect(-1, -1, -1, -1);
private boolean mAnimateNextBackgroundBottom;
private boolean mAnimateNextBackgroundTop;
private ObjectAnimator mBottomAnimator = null;
private ObjectAnimator mTopAnimator = null;
private ActivatableNotificationView mFirstVisibleBackgroundChild = null;
private ActivatableNotificationView mLastVisibleBackgroundChild = null;
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) {
mDimAnimator = null;
}
};
private ValueAnimator.AnimatorUpdateListener mDimUpdateListener
= new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
setDimAmount((Float) animation.getAnimatedValue());
}
};
protected 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 {
return 0;
}
}
};
private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
private boolean mPulsing;
private boolean mDrawBackgroundAsSrc;
private boolean mFadingOut;
private boolean mParentNotFullyVisible;
private boolean mGroupExpandedForMeasure;
private boolean mScrollable;
private View mForcedScroll;
private View mNeedingPulseAnimation;
private float mInterpolatedDarkAmount = 0f;
private float mLinearDarkAmount = 0f;
private float mBackgroundXFactor = 1f;
private boolean mUsingLightTheme;
private boolean mQsExpanded;
private boolean mForwardScrollable;
private boolean mBackwardScrollable;
private NotificationShelf mShelf;
private int mMaxDisplayedNotifications = -1;
private int mStatusBarHeight;
private int mMinInteractionHeight;
private boolean mNoAmbient;
private final Rect mClipRect = new Rect();
private boolean mIsClipped;
private Rect mRequestedClipBounds;
private boolean mInHeadsUpPinnedMode;
private boolean mHeadsUpAnimatingAway;
private int mStatusBarState;
private int mCachedBackgroundColor;
private boolean mHeadsUpGoingAwayAnimationsAllowed = true;
private Runnable mAnimateScroll = this::animateScroll;
private int mCornerRadius;
private int mSidePaddings;
private final int mSeparatorWidth;
private final int mSeparatorThickness;
private final Rect mBackgroundAnimationRect = new Rect();
private int mAntiBurnInOffsetX;
private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>();
private int mHeadsUpInset;
private HeadsUpAppearanceController mHeadsUpAppearanceController;
private NotificationIconAreaController mIconAreaController;
private float mVerticalPanelTranslation;
private Interpolator mDarkXInterpolator = Interpolators.FAST_OUT_SLOW_IN;
public NotificationStackScrollLayout(Context context) {
this(context, null);
}
public NotificationStackScrollLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
Resources res = getResources();
mAmbientState = new AmbientState(context);
mBgColor = context.getColor(R.color.notification_shade_background_color);
int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
mExpandHelper = new ExpandHelper(getContext(), this,
minHeight, maxHeight);
mExpandHelper.setEventSource(this);
mExpandHelper.setScrollAdapter(this);
mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, this, getContext());
mStackScrollAlgorithm = createStackScrollAlgorithm(context);
initView(context);
mFalsingManager = FalsingManager.getInstance(context);
mShouldDrawNotificationBackground =
res.getBoolean(R.bool.config_drawNotificationBackground);
mFadeNotificationsOnDismiss =
res.getBoolean(R.bool.config_fadeNotificationsOnDismiss);
mSeparatorWidth = res.getDimensionPixelSize(R.dimen.widget_separator_width);
mSeparatorThickness = res.getDimensionPixelSize(R.dimen.widget_separator_thickness);
mDarkSeparatorPadding = res.getDimensionPixelSize(R.dimen.widget_bottom_separator_padding);
mRoundnessManager.setAnimatedChildren(mChildrenToAddAnimated);
mRoundnessManager.setOnRoundingChangedCallback(this::invalidate);
addOnExpandedHeightListener(mRoundnessManager::setExpanded);
NotificationBlockingHelperManager blockingHelperManager =
Dependency.get(NotificationBlockingHelperManager.class);
addOnExpandedHeightListener((height, unused) -> {
blockingHelperManager.setNotificationShadeExpanded(height);
});
updateWillNotDraw();
mBackgroundPaint.setAntiAlias(true);
if (DEBUG) {
mDebugPaint = new Paint();
mDebugPaint.setColor(0xffff0000);
mDebugPaint.setStrokeWidth(2);
mDebugPaint.setStyle(Paint.Style.STROKE);
}
}
@Override
public NotificationSwipeActionHelper getSwipeActionHelper() {
return mSwipeHelper;
}
@Override
public void onMenuClicked(View view, int x, int y, MenuItem item) {
if (mLongPressListener == null) {
return;
}
if (view instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
MetricsLogger.action(mContext, MetricsEvent.ACTION_TOUCH_GEAR,
row.getStatusBarNotification().getPackageName());
}
mLongPressListener.onLongPress(view, x, y, item);
}
@Override
public void onMenuReset(View row) {
if (mTranslatingParentView != null && row == mTranslatingParentView) {
mMenuExposedView = null;
mTranslatingParentView = null;
}
}
@Override
public void onMenuShown(View row) {
mMenuExposedView = mTranslatingParentView;
if (row instanceof ExpandableNotificationRow) {
MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR,
((ExpandableNotificationRow) row).getStatusBarNotification()
.getPackageName());
}
mSwipeHelper.onMenuShown(row);
}
protected void onDraw(Canvas canvas) {
if (mShouldDrawNotificationBackground
&& (mCurrentBounds.top < mCurrentBounds.bottom || mAmbientState.isDark())) {
drawBackground(canvas);
}
if (DEBUG) {
int y = mTopPadding;
canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
y = getLayoutHeight();
canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
y = getHeight() - getEmptyBottomMargin();
canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
}
}
private void drawBackground(Canvas canvas) {
final int lockScreenLeft = mSidePaddings;
final int lockScreenRight = getWidth() - mSidePaddings;
final int lockScreenTop = mCurrentBounds.top;
final int lockScreenBottom = mCurrentBounds.bottom;
int separatorWidth = 0;
int separatorThickness = 0;
if (mIconAreaController.hasShelfIconsWhenFullyDark()) {
separatorThickness = mSeparatorThickness;
separatorWidth = mSeparatorWidth;
}
final int darkLeft = getWidth() / 2 - separatorWidth / 2;
final int darkRight = darkLeft + separatorWidth;
final int darkTop = (int) (mRegularTopPadding + separatorThickness / 2f);
final int darkBottom = darkTop + separatorThickness;
if (mAmbientState.hasPulsingNotifications()) {
} else if (mAmbientState.isFullyDark()) {
if (mFirstVisibleBackgroundChild != null) {
canvas.drawRect(darkLeft, darkTop, darkRight, darkBottom, mBackgroundPaint);
}
} else {
float yProgress = 1 - mInterpolatedDarkAmount;
float xProgress = mDarkXInterpolator.getInterpolation(
(1 - mLinearDarkAmount) * mBackgroundXFactor);
mBackgroundAnimationRect.set(
(int) MathUtils.lerp(darkLeft, lockScreenLeft, xProgress),
(int) MathUtils.lerp(darkTop, lockScreenTop, yProgress),
(int) MathUtils.lerp(darkRight, lockScreenRight, xProgress),
(int) MathUtils.lerp(darkBottom, lockScreenBottom, yProgress));
if (!mAmbientState.isDark() || mFirstVisibleBackgroundChild != null) {
canvas.drawRoundRect(mBackgroundAnimationRect.left, mBackgroundAnimationRect.top,
mBackgroundAnimationRect.right, mBackgroundAnimationRect.bottom,
mCornerRadius, mCornerRadius, mBackgroundPaint);
}
}
updateClipping();
}
private void updateBackgroundDimming() {
if (!mShouldDrawNotificationBackground) {
return;
}
float alpha =
BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount);
alpha *= 1f - mInterpolatedDarkAmount;
int scrimColor = mScrimController.getBackgroundColor();
int awakeColor = ColorUtils.blendARGB(scrimColor, mBgColor, alpha);
float colorInterpolation = Interpolators.DECELERATE_QUINT.getInterpolation(
mInterpolatedDarkAmount);
int color = ColorUtils.blendARGB(awakeColor, Color.WHITE, colorInterpolation);
if (mCachedBackgroundColor != color) {
mCachedBackgroundColor = color;
mBackgroundPaint.setColor(color);
invalidate();
}
}
private void initView(Context context) {
mScroller = new OverScroller(getContext());
setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
setClipChildren(false);
final ViewConfiguration configuration = ViewConfiguration.get(context);
mTouchSlop = configuration.getScaledTouchSlop();
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
mOverflingDistance = configuration.getScaledOverflingDistance();
Resources res = context.getResources();
mCollapsedSize = res.getDimensionPixelSize(R.dimen.notification_min_height);
mStackScrollAlgorithm.initView(context);
mAmbientState.reload(context);
mPaddingBetweenElements = Math.max(1,
res.getDimensionPixelSize(R.dimen.notification_divider_height));
mIncreasedPaddingBetweenElements =
res.getDimensionPixelSize(R.dimen.notification_divider_height_increased);
mMinTopOverScrollToEscape = res.getDimensionPixelSize(
R.dimen.min_top_overscroll_to_qs);
mStatusBarHeight = res.getDimensionPixelSize(R.dimen.status_bar_height);
mBottomMargin = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom);
mSidePaddings = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
mMinInteractionHeight = res.getDimensionPixelSize(
R.dimen.notification_min_interaction_height);
mCornerRadius = res.getDimensionPixelSize(
Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
mHeadsUpInset = mStatusBarHeight + res.getDimensionPixelSize(
R.dimen.heads_up_status_bar_padding);
}
public void setDrawBackgroundAsSrc(boolean asSrc) {
mDrawBackgroundAsSrc = asSrc;
updateSrcDrawing();
}
private void updateSrcDrawing() {
if (!mShouldDrawNotificationBackground) {
return;
}
mBackgroundPaint.setXfermode(mDrawBackgroundAsSrc && !mFadingOut && !mParentNotFullyVisible
? mSrcMode : null);
invalidate();
}
private void notifyHeightChangeListener(ExpandableView view) {
notifyHeightChangeListener(view, false );
}
private void notifyHeightChangeListener(ExpandableView view, boolean needsAnimation) {
if (mOnHeightChangedListener != null) {
mOnHeightChangedListener.onHeightChanged(view, needsAnimation);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int childWidthSpec = MeasureSpec.makeMeasureSpec(width - mSidePaddings * 2,
MeasureSpec.getMode(widthMeasureSpec));
final int size = getChildCount();
for (int i = 0; i < size; i++) {
measureChild(getChildAt(i), childWidthSpec, heightMeasureSpec);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
float centerX = getWidth() / 2.0f;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
float width = child.getMeasuredWidth();
float height = child.getMeasuredHeight();
child.layout((int) (centerX - width / 2.0f),
0,
(int) (centerX + width / 2.0f),
(int) height);
}
setMaxLayoutHeight(getHeight());
updateContentHeight();
clampScrollPosition();
requestChildrenUpdate();
updateFirstAndLastBackgroundViews();
updateAlgorithmLayoutMinHeight();
}
private void requestAnimationOnViewResize(ExpandableNotificationRow row) {
if (mAnimationsEnabled && (mIsExpanded || row != null && row.isPinned())) {
mNeedViewResizeAnimation = true;
mNeedsAnimation = true;
}
}
public void updateSpeedBumpIndex(int newIndex, boolean noAmbient) {
mAmbientState.setSpeedBumpIndex(newIndex);
mNoAmbient = noAmbient;
}
@Override
public void setChildLocationsChangedListener(
NotificationLogger.OnChildLocationsChangedListener listener) {
mListener = listener;
}
@Override
public boolean isInVisibleLocation(ExpandableNotificationRow row) {
ExpandableViewState childViewState = mCurrentStackScrollState.getViewStateForView(row);
if (childViewState == null) {
return false;
}
if ((childViewState.location & ExpandableViewState.VISIBLE_LOCATIONS) == 0) {
return false;
}
if (row.getVisibility() != View.VISIBLE) {
return false;
}
return true;
}
private void setMaxLayoutHeight(int maxLayoutHeight) {
mMaxLayoutHeight = maxLayoutHeight;
mShelf.setMaxLayoutHeight(maxLayoutHeight);
updateAlgorithmHeightAndPadding();
}
private void updateAlgorithmHeightAndPadding() {
mTopPadding = (int) MathUtils.lerp(mRegularTopPadding, mDarkTopPadding,
mInterpolatedDarkAmount);
mAmbientState.setLayoutHeight(getLayoutHeight());
updateAlgorithmLayoutMinHeight();
mAmbientState.setTopPadding(mTopPadding);
}
private void updateAlgorithmLayoutMinHeight() {
mAmbientState.setLayoutMinHeight(mQsExpanded || isHeadsUpTransition()
? getLayoutMinHeight() : 0);
}
private void updateChildren() {
updateScrollStateForAddedChildren();
mAmbientState.setCurrentScrollVelocity(mScroller.isFinished()
? 0
: mScroller.getCurrVelocity());
mAmbientState.setScrollY(mOwnScrollY);
mStackScrollAlgorithm.getStackScrollState(mAmbientState, mCurrentStackScrollState);
if (!isCurrentlyAnimating() && !mNeedsAnimation) {
applyCurrentState();
} else {
startAnimationToState();
}
}
private void onPreDrawDuringAnimation() {
mShelf.updateAppearance();
updateClippingToTopRoundedCorner();
if (!mNeedsAnimation && !mChildrenUpdateRequested) {
updateBackground();
}
}
private void updateClippingToTopRoundedCorner() {
Float clipStart = (float) mTopPadding
+ mStackTranslation
+ mAmbientState.getExpandAnimationTopChange();
Float clipEnd = clipStart + mCornerRadius;
boolean first = true;
for (int i = 0; i < getChildCount(); i++) {
ExpandableView child = (ExpandableView) getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
float start = child.getTranslationY();
float end = start + child.getActualHeight();
boolean clip = clipStart > start && clipStart < end
|| clipEnd >= start && clipEnd <= end;
clip &= !(first && mOwnScrollY == 0);
child.setDistanceToTopRoundness(clip ? Math.max(start - clipStart, 0)
: ExpandableView.NO_ROUNDNESS);
first = false;
}
}
private void updateScrollStateForAddedChildren() {
if (mChildrenToAddAnimated.isEmpty()) {
return;
}
for (int i = 0; i < getChildCount(); i++) {
ExpandableView child = (ExpandableView) getChildAt(i);
if (mChildrenToAddAnimated.contains(child)) {
int startingPosition = getPositionInLinearLayout(child);
float increasedPaddingAmount = child.getIncreasedPaddingAmount();
int padding = increasedPaddingAmount == 1.0f ? mIncreasedPaddingBetweenElements
: increasedPaddingAmount == -1.0f ? 0 : mPaddingBetweenElements;
int childHeight = getIntrinsicHeight(child) + padding;
if (startingPosition < mOwnScrollY) {
setOwnScrollY(mOwnScrollY + childHeight);
}
}
}
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);
int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
targetScroll = Math.max(0, Math.min(targetScroll, getScrollRange()));
if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
setOwnScrollY(targetScroll);
}
}
}
private void requestChildrenUpdate() {
if (!mChildrenUpdateRequested) {
getViewTreeObserver().addOnPreDrawListener(mChildrenUpdater);
mChildrenUpdateRequested = true;
invalidate();
}
}
private boolean isCurrentlyAnimating() {
return mStateAnimator.isRunning();
}
private void clampScrollPosition() {
int scrollRange = getScrollRange();
if (scrollRange < mOwnScrollY) {
setOwnScrollY(scrollRange);
}
}
public int getTopPadding() {
return mTopPadding;
}
private void setTopPadding(int topPadding, boolean animate) {
if (mRegularTopPadding != topPadding) {
mRegularTopPadding = topPadding;
mDarkTopPadding = topPadding + mDarkSeparatorPadding;
mAmbientState.setDarkTopPadding(mDarkTopPadding);
updateAlgorithmHeightAndPadding();
updateContentHeight();
if (animate && mAnimationsEnabled && mIsExpanded) {
mTopPaddingNeedsAnimation = true;
mNeedsAnimation = true;
}
requestChildrenUpdate();
notifyHeightChangeListener(null, animate);
}
}
public void setExpandedHeight(float height) {
mExpandedHeight = height;
setIsExpanded(height > 0);
int minExpansionHeight = getMinExpansionHeight();
if (height < minExpansionHeight) {
mClipRect.left = 0;
mClipRect.right = getWidth();
mClipRect.top = 0;
mClipRect.bottom = (int) height;
height = minExpansionHeight;
setRequestedClipBounds(mClipRect);
} else {
setRequestedClipBounds(null);
}
int stackHeight;
float translationY;
float appearEndPosition = getAppearEndPosition();
float appearStartPosition = getAppearStartPosition();
float appearFraction = 1.0f;
boolean appearing = height < appearEndPosition;
mAmbientState.setAppearing(appearing);
if (!appearing) {
translationY = 0;
if (mShouldShowShelfOnly) {
stackHeight = mTopPadding + mShelf.getIntrinsicHeight();
} else if (mQsExpanded) {
int stackStartPosition = mContentHeight - mTopPadding + mIntrinsicPadding;
int stackEndPosition = mMaxTopPadding + mShelf.getIntrinsicHeight();
if (stackStartPosition <= stackEndPosition) {
stackHeight = stackEndPosition;
} else {
stackHeight = (int) NotificationUtils.interpolate(stackStartPosition,
stackEndPosition, mQsExpansionFraction);
}
} else {
stackHeight = (int) height;
}
} else {
appearFraction = getAppearFraction(height);
if (appearFraction >= 0) {
translationY = NotificationUtils.interpolate(getExpandTranslationStart(), 0,
appearFraction);
} else {
translationY = height - appearStartPosition + getExpandTranslationStart();
}
if (isHeadsUpTransition()) {
stackHeight = mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight();
translationY = MathUtils.lerp(mHeadsUpInset - mTopPadding, 0, appearFraction);
} else {
stackHeight = (int) (height - translationY);
}
}
if (stackHeight != mCurrentStackHeight) {
mCurrentStackHeight = stackHeight;
updateAlgorithmHeightAndPadding();
requestChildrenUpdate();
}
setStackTranslation(translationY);
for (int i = 0; i < mExpandedHeightListeners.size(); i++) {
BiConsumer<Float, Float> listener = mExpandedHeightListeners.get(i);
listener.accept(mExpandedHeight, appearFraction);
}
}
private void setRequestedClipBounds(Rect clipRect) {
mRequestedClipBounds = clipRect;
updateClipping();
}
public int getIntrinsicContentHeight() {
return mIntrinsicContentHeight;
}
public void updateClipping() {
boolean animatingClipping = mInterpolatedDarkAmount > 0 && mInterpolatedDarkAmount < 1;
boolean clipped = mRequestedClipBounds != null && !mInHeadsUpPinnedMode
&& !mHeadsUpAnimatingAway;
if (mIsClipped != clipped) {
mIsClipped = clipped;
updateFadingState();
}
if (animatingClipping) {
setClipBounds(mBackgroundAnimationRect);
} else if (clipped) {
setClipBounds(mRequestedClipBounds);
} else {
setClipBounds(null);
}
}
private float getExpandTranslationStart() {
return - mTopPadding;
}
private float getAppearStartPosition() {
if (isHeadsUpTransition()) {
return mHeadsUpInset + mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight();
}
return getMinExpansionHeight();
}
private int getTopHeadsUpPinnedHeight() {
NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
if (topEntry == null) {
return 0;
}
ExpandableNotificationRow row = topEntry.row;
if (row.isChildInGroup()) {
final ExpandableNotificationRow groupSummary
= mGroupManager.getGroupSummary(row.getStatusBarNotification());
if (groupSummary != null) {
row = groupSummary;
}
}
return row.getPinnedHeadsUpHeight();
}
private float getAppearEndPosition() {
int appearPosition;
int notGoneChildCount = getNotGoneChildCount();
if (mEmptyShadeView.getVisibility() == GONE && notGoneChildCount != 0) {
if (isHeadsUpTransition()
|| (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDark())) {
appearPosition = getTopHeadsUpPinnedHeight();
} else {
appearPosition = 0;
if (notGoneChildCount >= 1 && mShelf.getVisibility() != GONE) {
appearPosition += mShelf.getIntrinsicHeight();
}
}
} else {
appearPosition = mEmptyShadeView.getHeight();
}
return appearPosition + (onKeyguard() ? mTopPadding : mIntrinsicPadding);
}
private boolean isHeadsUpTransition() {
return mTrackingHeadsUp && mFirstVisibleBackgroundChild != null
&& mAmbientState.isAboveShelf(mFirstVisibleBackgroundChild);
}
public float getAppearFraction(float height) {
float appearEndPosition = getAppearEndPosition();
float appearStartPosition = getAppearStartPosition();
return (height - appearStartPosition)
/ (appearEndPosition - appearStartPosition);
}
public float getStackTranslation() {
return mStackTranslation;
}
private void setStackTranslation(float stackTranslation) {
if (stackTranslation != mStackTranslation) {
mStackTranslation = stackTranslation;
mAmbientState.setStackTranslation(stackTranslation);
requestChildrenUpdate();
}
}
private int getLayoutHeight() {
return Math.min(mMaxLayoutHeight, mCurrentStackHeight);
}
public int getFirstItemMinHeight() {
final ExpandableView firstChild = getFirstChildNotGone();
return firstChild != null ? firstChild.getMinHeight() : mCollapsedSize;
}
public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) {
mLongPressListener = listener;
}
public void setQsContainer(ViewGroup qsContainer) {
mQsContainer = qsContainer;
}
@Override
public void onChildDismissed(View view) {
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
if (!row.isDismissed()) {
handleChildViewDismissed(view);
}
ViewGroup transientContainer = row.getTransientContainer();
if (transientContainer != null) {
transientContainer.removeTransientView(view);
}
}
private void handleChildViewDismissed(View view) {
if (mDismissAllInProgress) {
return;
}
boolean isBlockingHelperShown = false;
setSwipingInProgress(false);
if (mDragAnimPendingChildren.contains(view)) {
mDragAnimPendingChildren.remove(view);
}
mAmbientState.onDragFinished(view);
updateContinuousShadowDrawing();
if (view instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
if (row.isHeadsUp()) {
mHeadsUpManager.addSwipedOutNotification(row.getStatusBarNotification().getKey());
}
isBlockingHelperShown =
row.performDismissWithBlockingHelper(false );
}
if (!isBlockingHelperShown) {
mSwipedOutViews.add(view);
}
mFalsingManager.onNotificationDismissed();
if (mFalsingManager.shouldEnforceBouncer()) {
mStatusBar.executeRunnableDismissingKeyguard(
null,
null ,
false ,
true ,
false );
}
}
@Override
public void onChildSnappedBack(View animView, float targetLeft) {
mAmbientState.onDragFinished(animView);
updateContinuousShadowDrawing();
if (!mDragAnimPendingChildren.contains(animView)) {
if (mAnimationsEnabled) {
mSnappedBackChildren.add(animView);
mNeedsAnimation = true;
}
requestChildrenUpdate();
} else {
mDragAnimPendingChildren.remove(animView);
}
if (mCurrMenuRow != null && targetLeft == 0) {
mCurrMenuRow.resetMenu();
mCurrMenuRow = null;
}
}
@Override
public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
return !mFadeNotificationsOnDismiss;
}
@Override
public void onBeginDrag(View v) {
mFalsingManager.onNotificatonStartDismissing();
setSwipingInProgress(true);
mAmbientState.onBeginDrag(v);
updateContinuousShadowDrawing();
if (mAnimationsEnabled && (mIsExpanded || !isPinnedHeadsUp(v))) {
mDragAnimPendingChildren.add(v);
mNeedsAnimation = true;
}
requestChildrenUpdate();
}
public static boolean isPinnedHeadsUp(View v) {
if (v instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) v;
return row.isHeadsUp() && row.isPinned();
}
return false;
}
private boolean isHeadsUp(View v) {
if (v instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) v;
return row.isHeadsUp();
}
return false;
}
@Override
public void onDragCancelled(View v) {
mFalsingManager.onNotificatonStopDismissing();
setSwipingInProgress(false);
}
@Override
public float getFalsingThresholdFactor() {
return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
}
@Override
public View getChildAtPosition(MotionEvent ev) {
View child = getChildAtPosition(ev.getX(), ev.getY());
if (child instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
ExpandableNotificationRow parent = row.getNotificationParent();
if (parent != null && parent.areChildrenExpanded()
&& (parent.areGutsExposed()
|| mMenuExposedView == parent
|| (parent.getNotificationChildren().size() == 1
&& parent.isClearable()))) {
child = parent;
}
}
return child;
}
public ExpandableView getClosestChildAtRawPosition(float touchX, float touchY) {
getLocationOnScreen(mTempInt2);
float localTouchY = touchY - mTempInt2[1];
ExpandableView closestChild = null;
float minDist = Float.MAX_VALUE;
final int count = getChildCount();
for (int childIdx = 0; childIdx < count; childIdx++) {
ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
if (slidingChild.getVisibility() == GONE
|| slidingChild instanceof StackScrollerDecorView) {
continue;
}
float childTop = slidingChild.getTranslationY();
float top = childTop + slidingChild.getClipTopAmount();
float bottom = childTop + slidingChild.getActualHeight()
- slidingChild.getClipBottomAmount();
float dist = Math.min(Math.abs(top - localTouchY), Math.abs(bottom - localTouchY));
if (dist < minDist) {
closestChild = slidingChild;
minDist = dist;
}
}
return closestChild;
}
@Override
public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
getLocationOnScreen(mTempInt2);
return getChildAtPosition(touchX - mTempInt2[0], touchY - mTempInt2[1]);
}
@Override
public ExpandableView getChildAtPosition(float touchX, float touchY) {
return getChildAtPosition(touchX, touchY, true );
}
private ExpandableView getChildAtPosition(float touchX, float touchY,
boolean requireMinHeight) {
final int count = getChildCount();
for (int childIdx = 0; childIdx < count; childIdx++) {
ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
if (slidingChild.getVisibility() != VISIBLE
|| slidingChild instanceof StackScrollerDecorView) {
continue;
}
float childTop = slidingChild.getTranslationY();
float top = childTop + slidingChild.getClipTopAmount();
float bottom = childTop + slidingChild.getActualHeight()
- slidingChild.getClipBottomAmount();
int left = 0;
int right = getWidth();
if ((bottom - top >= mMinInteractionHeight || !requireMinHeight)
&& touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
if (slidingChild instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
&& mHeadsUpManager.getTopEntry().row != row
&& mGroupManager.getGroupSummary(
mHeadsUpManager.getTopEntry().row.getStatusBarNotification())
!= row) {
continue;
}
return row.getViewAtPosition(touchY - childTop);
}
return slidingChild;
}
}
return null;
}
@Override
public boolean canChildBeExpanded(View v) {
return v instanceof ExpandableNotificationRow
&& ((ExpandableNotificationRow) v).isExpandable()
&& !((ExpandableNotificationRow) v).areGutsExposed()
&& (mIsExpanded || !((ExpandableNotificationRow) v).isPinned());
}
@Override
public void setUserExpandedChild(View v, boolean userExpanded) {
if (v instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) v;
if (userExpanded && onKeyguard()) {
row.setUserLocked(false);
updateContentHeight();
notifyHeightChangeListener(row);
return;
}
row.setUserExpanded(userExpanded, true );
row.onExpandedByGesture(userExpanded);
}
}
@Override
public void setExpansionCancelled(View v) {
if (v instanceof ExpandableNotificationRow) {
((ExpandableNotificationRow) v).setGroupExpansionChanging(false);
}
}
@Override
public void setUserLockedChild(View v, boolean userLocked) {
if (v instanceof ExpandableNotificationRow) {
((ExpandableNotificationRow) v).setUserLocked(userLocked);
}
cancelLongPress();
requestDisallowInterceptTouchEvent(true);
}
@Override
public void expansionStateChanged(boolean isExpanding) {
mExpandingNotification = isExpanding;
if (!mExpandedInThisMotion) {
mMaxScrollAfterExpand = mOwnScrollY;
mExpandedInThisMotion = true;
}
}
@Override
public int getMaxExpandHeight(ExpandableView view) {
return view.getMaxContentHeight();
}
public void setScrollingEnabled(boolean enable) {
mScrollingEnabled = enable;
}
public void lockScrollTo(View v) {
if (mForcedScroll == v) {
return;
}
mForcedScroll = v;
scrollTo(v);
}
public boolean scrollTo(View v) {
ExpandableView expandableView = (ExpandableView) v;
int positionInLinearLayout = getPositionInLinearLayout(v);
int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScroll - mOwnScrollY);
mDontReportNextOverScroll = true;
animateScroll();
return true;
}
return false;
}
private int targetScrollForView(ExpandableView v, int positionInLinearLayout) {
return positionInLinearLayout + v.getIntrinsicHeight() +
getImeInset() - getHeight()
+ ((!isExpanded() && isPinnedHeadsUp(v)) ? mHeadsUpInset : getTopPadding());
}
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
mBottomInset = insets.getSystemWindowInsetBottom();
int range = getScrollRange();
if (mOwnScrollY > range) {
removeCallbacks(mReclamp);
postDelayed(mReclamp, 50);
} else if (mForcedScroll != null) {
scrollTo(mForcedScroll);
}
return insets;
}
private Runnable mReclamp = new Runnable() {
@Override
public void run() {
int range = getScrollRange();
mScroller.startScroll(mScrollX, mOwnScrollY, 0, range - mOwnScrollY);
mDontReportNextOverScroll = true;
mDontClampNextScroll = true;
animateScroll();
}
};
public void setExpandingEnabled(boolean enable) {
mExpandHelper.setEnabled(enable);
}
private boolean isScrollingEnabled() {
return mScrollingEnabled;
}
@Override
public boolean canChildBeDismissed(View v) {
return StackScrollAlgorithm.canChildBeDismissed(v);
}
@Override
public boolean isAntiFalsingNeeded() {
return onKeyguard();
}
private boolean onKeyguard() {
return mStatusBarState == StatusBarState.KEYGUARD;
}
private void setSwipingInProgress(boolean isSwiped) {
mSwipingInProgress = isSwiped;
if(isSwiped) {
requestDisallowInterceptTouchEvent(true);
}
}
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mStatusBarHeight = getResources().getDimensionPixelOffset(R.dimen.status_bar_height);
float densityScale = getResources().getDisplayMetrics().density;
mSwipeHelper.setDensityScale(densityScale);
float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
initView(getContext());
}
public void dismissViewAnimated(View child, Runnable endRunnable, int delay, long duration) {
mSwipeHelper.dismissChild(child, 0, endRunnable, delay, true, duration,
true );
}
@Override
public void snapViewIfNeeded(ExpandableNotificationRow child) {
boolean animate = mIsExpanded || isPinnedHeadsUp(child);
float targetLeft = child.getProvider().isMenuVisible() ? child.getTranslation() : 0;
mSwipeHelper.snapChildIfNeeded(child, animate, targetLeft);
}
@Override
public ViewGroup getViewParentForNotification(NotificationData.Entry entry) {
return this;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
|| ev.getActionMasked()== MotionEvent.ACTION_UP;
handleEmptySpaceClick(ev);
boolean expandWantsIt = false;
if (mIsExpanded && !mSwipingInProgress && !mOnlyScrollingInThisMotion) {
if (isCancelOrUp) {
mExpandHelper.onlyObserveMovements(false);
}
boolean wasExpandingBefore = mExpandingNotification;
expandWantsIt = mExpandHelper.onTouchEvent(ev);
if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore
&& !mDisallowScrollingInThisMotion) {
dispatchDownEventToScroller(ev);
}
}
boolean scrollerWantsIt = false;
if (mIsExpanded && !mSwipingInProgress && !mExpandingNotification
&& !mDisallowScrollingInThisMotion) {
scrollerWantsIt = onScrollTouch(ev);
}
boolean horizontalSwipeWantsIt = false;
if (!mIsBeingDragged
&& !mExpandingNotification
&& !mExpandedInThisMotion
&& !mOnlyScrollingInThisMotion
&& !mDisallowDismissInThisMotion) {
horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
}
NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
if (guts != null && !isTouchInView(ev, guts)
&& guts.getGutsContent() instanceof NotificationSnooze) {
NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
if ((ns.isExpanded() && isCancelOrUp)
|| (!horizontalSwipeWantsIt && scrollerWantsIt)) {
checkSnoozeLeavebehind();
}
}
if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
mCheckForLeavebehind = true;
}
return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
}
private void dispatchDownEventToScroller(MotionEvent ev) {
MotionEvent downEvent = MotionEvent.obtain(ev);
downEvent.setAction(MotionEvent.ACTION_DOWN);
onScrollTouch(downEvent);
downEvent.recycle();
}
@Override
public boolean onGenericMotionEvent(MotionEvent event) {
if (!isScrollingEnabled() || !mIsExpanded || mSwipingInProgress || mExpandingNotification
|| mDisallowScrollingInThisMotion) {
return false;
}
if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
switch (event.getAction()) {
case MotionEvent.ACTION_SCROLL: {
if (!mIsBeingDragged) {
final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
if (vscroll != 0) {
final int delta = (int) (vscroll * getVerticalScrollFactor());
final int range = getScrollRange();
int oldScrollY = mOwnScrollY;
int newScrollY = oldScrollY - delta;
if (newScrollY < 0) {
newScrollY = 0;
} else if (newScrollY > range) {
newScrollY = range;
}
if (newScrollY != oldScrollY) {
setOwnScrollY(newScrollY);
return true;
}
}
}
}
}
}
return super.onGenericMotionEvent(event);
}
private boolean onScrollTouch(MotionEvent ev) {
if (!isScrollingEnabled()) {
return false;
}
if (isInsideQsContainer(ev) && !mIsBeingDragged) {
return false;
}
mForcedScroll = null;
initVelocityTrackerIfNotExists();
mVelocityTracker.addMovement(ev);
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
if (getChildCount() == 0 || !isInContentBounds(ev)) {
return false;
}
boolean isBeingDragged = !mScroller.isFinished();
setIsBeingDragged(isBeingDragged);
if (!mScroller.isFinished()) {
mScroller.forceFinished(true);
}
mLastMotionY = (int) ev.getY();
mDownX = (int) ev.getX();
mActivePointerId = ev.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE:
final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
if (activePointerIndex == -1) {
Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
break;
}
final int y = (int) ev.getY(activePointerIndex);
final int x = (int) ev.getX(activePointerIndex);
int deltaY = mLastMotionY - y;
final int xDiff = Math.abs(x - mDownX);
final int yDiff = Math.abs(deltaY);
if (!mIsBeingDragged && yDiff > mTouchSlop && yDiff > xDiff) {
setIsBeingDragged(true);
if (deltaY > 0) {
deltaY -= mTouchSlop;
} else {
deltaY += mTouchSlop;
}
}
if (mIsBeingDragged) {
mLastMotionY = y;
int range = getScrollRange();
if (mExpandedInThisMotion) {
range = Math.min(range, mMaxScrollAfterExpand);
}
float scrollAmount;
if (deltaY < 0) {
scrollAmount = overScrollDown(deltaY);
} else {
scrollAmount = overScrollUp(deltaY, range);
}
if (scrollAmount != 0.0f) {
customOverScrollBy((int) scrollAmount, mOwnScrollY,
range, getHeight() / 2);
checkSnoozeLeavebehind();
}
}
break;
case MotionEvent.ACTION_UP:
if (mIsBeingDragged) {
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
if (shouldOverScrollFling(initialVelocity)) {
onOverScrollFling(true, initialVelocity);
} else {
if (getChildCount() > 0) {
if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
float currentOverScrollTop = getCurrentOverScrollAmount(true);
if (currentOverScrollTop == 0.0f || initialVelocity > 0) {
fling(-initialVelocity);
} else {
onOverScrollFling(false, initialVelocity);
}
} else {
if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
getScrollRange())) {
animateScroll();
}
}
}
}
mActivePointerId = INVALID_POINTER;
endDrag();
}
break;
case MotionEvent.ACTION_CANCEL:
if (mIsBeingDragged && getChildCount() > 0) {
if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
animateScroll();
}
mActivePointerId = INVALID_POINTER;
endDrag();
}
break;
case MotionEvent.ACTION_POINTER_DOWN: {
final int index = ev.getActionIndex();
mLastMotionY = (int) ev.getY(index);
mDownX = (int) ev.getX(index);
mActivePointerId = ev.getPointerId(index);
break;
}
case MotionEvent.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
mDownX = (int) ev.getX(ev.findPointerIndex(mActivePointerId));
break;
}
return true;
}
protected boolean isInsideQsContainer(MotionEvent ev) {
return ev.getY() < mQsContainer.getBottom();
}
private void onOverScrollFling(boolean open, int initialVelocity) {
if (mOverscrollTopChangedListener != null) {
mOverscrollTopChangedListener.flingTopOverscroll(initialVelocity, open);
}
mDontReportNextOverScroll = true;
setOverScrollAmount(0.0f, true, false);
}
private float overScrollUp(int deltaY, int range) {
deltaY = Math.max(deltaY, 0);
float currentTopAmount = getCurrentOverScrollAmount(true);
float newTopAmount = currentTopAmount - deltaY;
if (currentTopAmount > 0) {
setOverScrollAmount(newTopAmount, true ,
false );
}
float scrollAmount = newTopAmount < 0 ? -newTopAmount : 0.0f;
float newScrollY = mOwnScrollY + scrollAmount;
if (newScrollY > range) {
if (!mExpandedInThisMotion) {
float currentBottomPixels = getCurrentOverScrolledPixels(false);
setOverScrolledPixels(currentBottomPixels + newScrollY - range,
false ,
false );
}
setOwnScrollY(range);
scrollAmount = 0.0f;
}
return scrollAmount;
}
private float overScrollDown(int deltaY) {
deltaY = Math.min(deltaY, 0);
float currentBottomAmount = getCurrentOverScrollAmount(false);
float newBottomAmount = currentBottomAmount + deltaY;
if (currentBottomAmount > 0) {
setOverScrollAmount(newBottomAmount, false ,
false );
}
float scrollAmount = newBottomAmount < 0 ? newBottomAmount : 0.0f;
float newScrollY = mOwnScrollY + scrollAmount;
if (newScrollY < 0) {
float currentTopPixels = getCurrentOverScrolledPixels(true);
setOverScrolledPixels(currentTopPixels - newScrollY,
true ,
false );
setOwnScrollY(0);
scrollAmount = 0.0f;
}
return scrollAmount;
}
private void onSecondaryPointerUp(MotionEvent ev) {
final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastMotionY = (int) ev.getY(newPointerIndex);
mActivePointerId = ev.getPointerId(newPointerIndex);
if (mVelocityTracker != null) {
mVelocityTracker.clear();
}
}
}
private void initVelocityTrackerIfNotExists() {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
}
private void recycleVelocityTracker() {
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
private void initOrResetVelocityTracker() {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
} else {
mVelocityTracker.clear();
}
}
public void setFinishScrollingCallback(Runnable runnable) {
mFinishScrollingCallback = runnable;
}
private void animateScroll() {
if (mScroller.computeScrollOffset()) {
int oldY = mOwnScrollY;
int y = mScroller.getCurrY();
if (oldY != y) {
int range = getScrollRange();
if (y < 0 && oldY >= 0 || y > range && oldY <= range) {
float currVelocity = mScroller.getCurrVelocity();
if (currVelocity >= mMinimumVelocity) {
mMaxOverScroll = Math.abs(currVelocity) / 1000 * mOverflingDistance;
}
}
if (mDontClampNextScroll) {
range = Math.max(range, oldY);
}
customOverScrollBy(y - oldY, oldY, range,
(int) (mMaxOverScroll));
}
postOnAnimation(mAnimateScroll);
} else {
mDontClampNextScroll = false;
if (mFinishScrollingCallback != null) {
mFinishScrollingCallback.run();
}
}
}
private boolean customOverScrollBy(int deltaY, int scrollY, int scrollRangeY,
int maxOverScrollY) {
int newScrollY = scrollY + deltaY;
final int top = -maxOverScrollY;
final int bottom = maxOverScrollY + scrollRangeY;
boolean clampedY = false;
if (newScrollY > bottom) {
newScrollY = bottom;
clampedY = true;
} else if (newScrollY < top) {
newScrollY = top;
clampedY = true;
}
onCustomOverScrolled(newScrollY, clampedY);
return clampedY;
}
public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) {
setOverScrollAmount(numPixels * getRubberBandFactor(onTop), onTop, animate, true);
}
public void setOverScrollAmount(float amount, boolean onTop, boolean animate) {
setOverScrollAmount(amount, onTop, animate, true);
}
public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
boolean cancelAnimators) {
setOverScrollAmount(amount, onTop, animate, cancelAnimators, isRubberbanded(onTop));
}
public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
boolean cancelAnimators, boolean isRubberbanded) {
if (cancelAnimators) {
mStateAnimator.cancelOverScrollAnimators(onTop);
}
setOverScrollAmountInternal(amount, onTop, animate, isRubberbanded);
}
private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate,
boolean isRubberbanded) {
amount = Math.max(0, amount);
if (animate) {
mStateAnimator.animateOverScrollToAmount(amount, onTop, isRubberbanded);
} else {
setOverScrolledPixels(amount / getRubberBandFactor(onTop), onTop);
mAmbientState.setOverScrollAmount(amount, onTop);
if (onTop) {
notifyOverscrollTopListener(amount, isRubberbanded);
}
requestChildrenUpdate();
}
}
private void notifyOverscrollTopListener(float amount, boolean isRubberbanded) {
mExpandHelper.onlyObserveMovements(amount > 1.0f);
if (mDontReportNextOverScroll) {
mDontReportNextOverScroll = false;
return;
}
if (mOverscrollTopChangedListener != null) {
mOverscrollTopChangedListener.onOverscrollTopChanged(amount, isRubberbanded);
}
}
public void setOverscrollTopChangedListener(
OnOverscrollTopChangedListener overscrollTopChangedListener) {
mOverscrollTopChangedListener = overscrollTopChangedListener;
}
public float getCurrentOverScrollAmount(boolean top) {
return mAmbientState.getOverScrollAmount(top);
}
public float getCurrentOverScrolledPixels(boolean top) {
return top? mOverScrolledTopPixels : mOverScrolledBottomPixels;
}
private void setOverScrolledPixels(float amount, boolean onTop) {
if (onTop) {
mOverScrolledTopPixels = amount;
} else {
mOverScrolledBottomPixels = amount;
}
}
private void onCustomOverScrolled(int scrollY, boolean clampedY) {
if (!mScroller.isFinished()) {
setOwnScrollY(scrollY);
if (clampedY) {
springBack();
} else {
float overScrollTop = getCurrentOverScrollAmount(true);
if (mOwnScrollY < 0) {
notifyOverscrollTopListener(-mOwnScrollY, isRubberbanded(true));
} else {
notifyOverscrollTopListener(overScrollTop, isRubberbanded(true));
}
}
} else {
setOwnScrollY(scrollY);
}
}
private void springBack() {
int scrollRange = getScrollRange();
boolean overScrolledTop = mOwnScrollY <= 0;
boolean overScrolledBottom = mOwnScrollY >= scrollRange;
if (overScrolledTop || overScrolledBottom) {
boolean onTop;
float newAmount;
if (overScrolledTop) {
onTop = true;
newAmount = -mOwnScrollY;
setOwnScrollY(0);
mDontReportNextOverScroll = true;
} else {
onTop = false;
newAmount = mOwnScrollY - scrollRange;
setOwnScrollY(scrollRange);
}
setOverScrollAmount(newAmount, onTop, false);
setOverScrollAmount(0.0f, onTop, true);
mScroller.forceFinished(true);
}
}
private int getScrollRange() {
int contentHeight = mContentHeight;
if (!isExpanded() && mHeadsUpManager.hasPinnedHeadsUp()) {
contentHeight = mHeadsUpInset + getTopHeadsUpPinnedHeight();
}
int scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight);
int imeInset = getImeInset();
scrollRange += Math.min(imeInset, Math.max(0, contentHeight - (getHeight() - imeInset)));
return scrollRange;
}
private int getImeInset() {
return Math.max(0, mBottomInset - (getRootView().getHeight() - getHeight()));
}
public ExpandableView getFirstChildNotGone() {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() != View.GONE && child != mShelf) {
return (ExpandableView) child;
}
}
return null;
}
public ExpandableView getViewBeforeView(ExpandableView view) {
ExpandableView previousView = null;
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child == view) {
return previousView;
}
if (child.getVisibility() != View.GONE) {
previousView = (ExpandableView) child;
}
}
return null;
}
private View getFirstChildBelowTranlsationY(float translationY, boolean ignoreChildren) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() == View.GONE) {
continue;
}
float rowTranslation = child.getTranslationY();
if (rowTranslation >= translationY) {
return child;
} else if (!ignoreChildren && child instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
List<ExpandableNotificationRow> notificationChildren =
row.getNotificationChildren();
for (int childIndex = 0; childIndex < notificationChildren.size();
childIndex++) {
ExpandableNotificationRow rowChild = notificationChildren.get(childIndex);
if (rowChild.getTranslationY() + rowTranslation >= translationY) {
return rowChild;
}
}
}
}
}
return null;
}
public View getLastChildNotGone() {
int childCount = getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
View child = getChildAt(i);
if (child.getVisibility() != View.GONE && child != mShelf) {
return child;
}
}
return null;
}
public int getNotGoneChildCount() {
int childCount = getChildCount();
int count = 0;
for (int i = 0; i < childCount; i++) {
ExpandableView child = (ExpandableView) getChildAt(i);
if (child.getVisibility() != View.GONE && !child.willBeGone() && child != mShelf) {
count++;
}
}
return count;
}
private void updateContentHeight() {
int height = 0;
float previousPaddingRequest = mPaddingBetweenElements;
float previousPaddingAmount = 0.0f;
int numShownItems = 0;
boolean finish = false;
int maxDisplayedNotifications = mAmbientState.isFullyDark()
? (hasPulsingNotifications() ? 1 : 0)
: mMaxDisplayedNotifications;
for (int i = 0; i < getChildCount(); i++) {
ExpandableView expandableView = (ExpandableView) getChildAt(i);
boolean footerViewOnLockScreen = expandableView == mFooterView && onKeyguard();
if (expandableView.getVisibility() != View.GONE
&& !expandableView.hasNoContentHeight() && !footerViewOnLockScreen) {
boolean limitReached = maxDisplayedNotifications != -1
&& numShownItems >= maxDisplayedNotifications;
boolean notificationOnAmbientThatIsNotPulsing = mAmbientState.isFullyDark()
&& hasPulsingNotifications()
&& expandableView instanceof ExpandableNotificationRow
&& !isPulsing(((ExpandableNotificationRow) expandableView).getEntry());
if (limitReached || notificationOnAmbientThatIsNotPulsing) {
expandableView = mShelf;
finish = true;
}
float increasedPaddingAmount = expandableView.getIncreasedPaddingAmount();
float padding;
if (increasedPaddingAmount >= 0.0f) {
padding = (int) NotificationUtils.interpolate(
previousPaddingRequest,
mIncreasedPaddingBetweenElements,
increasedPaddingAmount);
previousPaddingRequest = (int) NotificationUtils.interpolate(
mPaddingBetweenElements,
mIncreasedPaddingBetweenElements,
increasedPaddingAmount);
} else {
int ownPadding = (int) NotificationUtils.interpolate(
0,
mPaddingBetweenElements,
1.0f + increasedPaddingAmount);
if (previousPaddingAmount > 0.0f) {
padding = (int) NotificationUtils.interpolate(
ownPadding,
mIncreasedPaddingBetweenElements,
previousPaddingAmount);
} else {
padding = ownPadding;
}
previousPaddingRequest = ownPadding;
}
if (height != 0) {
height += padding;
}
previousPaddingAmount = increasedPaddingAmount;
height += expandableView.getIntrinsicHeight();
numShownItems++;
if (finish) {
break;
}
}
}
mIntrinsicContentHeight = height;
int topPadding = mAmbientState.isFullyDark() ? mDarkTopPadding : mRegularTopPadding;
mContentHeight = height + topPadding + mBottomMargin;
updateScrollability();
clampScrollPosition();
mAmbientState.setLayoutMaxHeight(mContentHeight);
}
private boolean isPulsing(NotificationData.Entry entry) {
return mAmbientState.isPulsing(entry);
}
@Override
public boolean hasPulsingNotifications() {
return mPulsing;
}
private void updateScrollability() {
boolean scrollable = !mQsExpanded && getScrollRange() > 0;
if (scrollable != mScrollable) {
mScrollable = scrollable;
setFocusable(scrollable);
updateForwardAndBackwardScrollability();
}
}
private void updateForwardAndBackwardScrollability() {
boolean forwardScrollable = mScrollable && mOwnScrollY < getScrollRange();
boolean backwardsScrollable = mScrollable && mOwnScrollY > 0;
boolean changed = forwardScrollable != mForwardScrollable
|| backwardsScrollable != mBackwardScrollable;
mForwardScrollable = forwardScrollable;
mBackwardScrollable = backwardsScrollable;
if (changed) {
sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
}
}
private void updateBackground() {
if (!mShouldDrawNotificationBackground || mAmbientState.isFullyDark()) {
return;
}
updateBackgroundBounds();
if (!mCurrentBounds.equals(mBackgroundBounds)) {
boolean animate = mAnimateNextBackgroundTop || mAnimateNextBackgroundBottom
|| areBoundsAnimating();
if (!isExpanded()) {
abortBackgroundAnimators();
animate = false;
}
if (animate) {
startBackgroundAnimation();
} else {
mCurrentBounds.set(mBackgroundBounds);
applyCurrentBackgroundBounds();
}
} else {
abortBackgroundAnimators();
}
mAnimateNextBackgroundBottom = false;
mAnimateNextBackgroundTop = false;
}
private void abortBackgroundAnimators() {
if (mBottomAnimator != null) {
mBottomAnimator.cancel();
}
if (mTopAnimator != null) {
mTopAnimator.cancel();
}
}
private boolean areBoundsAnimating() {
return mBottomAnimator != null || mTopAnimator != null;
}
private void startBackgroundAnimation() {
mCurrentBounds.left = mBackgroundBounds.left;
mCurrentBounds.right = mBackgroundBounds.right;
startBottomAnimation();
startTopAnimation();
}
private void startTopAnimation() {
int previousEndValue = mEndAnimationRect.top;
int newEndValue = mBackgroundBounds.top;
ObjectAnimator previousAnimator = mTopAnimator;
if (previousAnimator != null && previousEndValue == newEndValue) {
return;
}
if (!mAnimateNextBackgroundTop) {
if (previousAnimator != null) {
int previousStartValue = mStartAnimationRect.top;
PropertyValuesHolder[] values = previousAnimator.getValues();
values[0].setIntValues(previousStartValue, newEndValue);
mStartAnimationRect.top = previousStartValue;
mEndAnimationRect.top = newEndValue;
previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
return;
} else {
setBackgroundTop(newEndValue);
return;
}
}
if (previousAnimator != null) {
previousAnimator.cancel();
}
ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundTop",
mCurrentBounds.top, newEndValue);
Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
animator.setInterpolator(interpolator);
animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mStartAnimationRect.top = -1;
mEndAnimationRect.top = -1;
mTopAnimator = null;
}
});
animator.start();
mStartAnimationRect.top = mCurrentBounds.top;
mEndAnimationRect.top = newEndValue;
mTopAnimator = animator;
}
private void startBottomAnimation() {
int previousStartValue = mStartAnimationRect.bottom;
int previousEndValue = mEndAnimationRect.bottom;
int newEndValue = mBackgroundBounds.bottom;
ObjectAnimator previousAnimator = mBottomAnimator;
if (previousAnimator != null && previousEndValue == newEndValue) {
return;
}
if (!mAnimateNextBackgroundBottom) {
if (previousAnimator != null) {
PropertyValuesHolder[] values = previousAnimator.getValues();
values[0].setIntValues(previousStartValue, newEndValue);
mStartAnimationRect.bottom = previousStartValue;
mEndAnimationRect.bottom = newEndValue;
previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
return;
} else {
setBackgroundBottom(newEndValue);
return;
}
}
if (previousAnimator != null) {
previousAnimator.cancel();
}
ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundBottom",
mCurrentBounds.bottom, newEndValue);
Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
animator.setInterpolator(interpolator);
animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mStartAnimationRect.bottom = -1;
mEndAnimationRect.bottom = -1;
mBottomAnimator = null;
}
});
animator.start();
mStartAnimationRect.bottom = mCurrentBounds.bottom;
mEndAnimationRect.bottom = newEndValue;
mBottomAnimator = animator;
}
private void setBackgroundTop(int top) {
mCurrentBounds.top = top;
applyCurrentBackgroundBounds();
}
public void setBackgroundBottom(int bottom) {
mCurrentBounds.bottom = bottom;
applyCurrentBackgroundBounds();
}
private void applyCurrentBackgroundBounds() {
if (!mShouldDrawNotificationBackground) {
return;
}
final boolean awake = mInterpolatedDarkAmount != 0 || mAmbientState.isDark();
mScrimController.setExcludedBackgroundArea(
mFadingOut || mParentNotFullyVisible || awake || mIsClipped ? null
: mCurrentBounds);
invalidate();
}
private void updateBackgroundBounds() {
getLocationInWindow(mTempInt2);
mBackgroundBounds.left = mTempInt2[0] + mSidePaddings;
mBackgroundBounds.right = mTempInt2[0] + getWidth() - mSidePaddings;
if (!mIsExpanded) {
mBackgroundBounds.top = 0;
mBackgroundBounds.bottom = 0;
return;
}
ActivatableNotificationView firstView = mFirstVisibleBackgroundChild;
int top = 0;
if (firstView != null) {
int finalTranslationY = (int) Math.ceil(ViewState.getFinalTranslationY(firstView));
if (mAnimateNextBackgroundTop
|| mTopAnimator == null && mCurrentBounds.top == finalTranslationY
|| mTopAnimator != null && mEndAnimationRect.top == finalTranslationY) {
top = finalTranslationY;
} else {
top = (int) Math.ceil(firstView.getTranslationY());
}
}
ActivatableNotificationView lastView =
mShelf.hasItemsInStableShelf() && mShelf.getVisibility() != GONE
? mShelf
: mLastVisibleBackgroundChild;
int bottom;
if (lastView != null) {
int finalTranslationY;
if (lastView == mShelf) {
finalTranslationY = (int) mShelf.getTranslationY();
} else {
finalTranslationY = (int) ViewState.getFinalTranslationY(lastView);
}
int finalHeight = ExpandableViewState.getFinalActualHeight(lastView);
int finalBottom = finalTranslationY + finalHeight - lastView.getClipBottomAmount();
if (mAnimateNextBackgroundBottom
|| mBottomAnimator == null && mCurrentBounds.bottom == finalBottom
|| mBottomAnimator != null && mEndAnimationRect.bottom == finalBottom) {
bottom = finalBottom;
} else {
bottom = (int) (lastView.getTranslationY() + lastView.getActualHeight()
- lastView.getClipBottomAmount());
}
} else {
top = mTopPadding;
bottom = top;
}
if (mStatusBarState != StatusBarState.KEYGUARD) {
top = (int) Math.max(mTopPadding + mStackTranslation, top);
} else {
top = Math.max(0, top);
}
mBackgroundBounds.top = top;
mBackgroundBounds.bottom = Math.max(bottom, top);
}
private ActivatableNotificationView getFirstPinnedHeadsUp() {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() != View.GONE
&& child instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
if (row.isPinned()) {
return row;
}
}
}
return null;
}
private ActivatableNotificationView getLastChildWithBackground() {
int childCount = getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
View child = getChildAt(i);
if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
&& child != mShelf) {
return (ActivatableNotificationView) child;
}
}
return null;
}
private ActivatableNotificationView getFirstChildWithBackground() {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
&& child != mShelf) {
return (ActivatableNotificationView) child;
}
}
return null;
}
protected void fling(int velocityY) {
if (getChildCount() > 0) {
int scrollRange = getScrollRange();
float topAmount = getCurrentOverScrollAmount(true);
float bottomAmount = getCurrentOverScrollAmount(false);
if (velocityY < 0 && topAmount > 0) {
setOwnScrollY(mOwnScrollY - (int) topAmount);
mDontReportNextOverScroll = true;
setOverScrollAmount(0, true, false);
mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor(true )
* mOverflingDistance + topAmount;
} else if (velocityY > 0 && bottomAmount > 0) {
setOwnScrollY((int) (mOwnScrollY + bottomAmount));
setOverScrollAmount(0, false, false);
mMaxOverScroll = Math.abs(velocityY) / 1000f
* getRubberBandFactor(false ) * mOverflingDistance
+ bottomAmount;
} else {
mMaxOverScroll = 0.0f;
}
int minScrollY = Math.max(0, scrollRange);
if (mExpandedInThisMotion) {
minScrollY = Math.min(minScrollY, mMaxScrollAfterExpand);
}
mScroller.fling(mScrollX, mOwnScrollY, 1, velocityY, 0, 0, 0, minScrollY, 0,
mExpandedInThisMotion && mOwnScrollY >= 0 ? 0 : Integer.MAX_VALUE / 2);
animateScroll();
}
}
private boolean shouldOverScrollFling(int initialVelocity) {
float topOverScroll = getCurrentOverScrollAmount(true);
return mScrolledToTopOnFirstDown
&& !mExpandedInThisMotion
&& topOverScroll > mMinTopOverScrollToEscape
&& initialVelocity > 0;
}
public void updateTopPadding(float qsHeight, boolean animate,
boolean ignoreIntrinsicPadding) {
int topPadding = (int) qsHeight;
int minStackHeight = getLayoutMinHeight();
if (topPadding + minStackHeight > getHeight()) {
mTopPaddingOverflow = topPadding + minStackHeight - getHeight();
} else {
mTopPaddingOverflow = 0;
}
setTopPadding(ignoreIntrinsicPadding ? topPadding : clampPadding(topPadding),
animate);
setExpandedHeight(mExpandedHeight);
}
public void setMaxTopPadding(int maxTopPadding) {
mMaxTopPadding = maxTopPadding;
}
public int getLayoutMinHeight() {
if (isHeadsUpTransition()) {
return getTopHeadsUpPinnedHeight();
}
return mShelf.getVisibility() == GONE ? 0 : mShelf.getIntrinsicHeight();
}
public int getFirstChildIntrinsicHeight() {
final ExpandableView firstChild = getFirstChildNotGone();
int firstChildMinHeight = firstChild != null
? firstChild.getIntrinsicHeight()
: mEmptyShadeView != null
? mEmptyShadeView.getIntrinsicHeight()
: mCollapsedSize;
if (mOwnScrollY > 0) {
firstChildMinHeight = Math.max(firstChildMinHeight - mOwnScrollY, mCollapsedSize);
}
return firstChildMinHeight;
}
public float getTopPaddingOverflow() {
return mTopPaddingOverflow;
}
public int getPeekHeight() {
final ExpandableView firstChild = getFirstChildNotGone();
final int firstChildMinHeight = firstChild != null ? firstChild.getCollapsedHeight()
: mCollapsedSize;
int shelfHeight = 0;
if (mLastVisibleBackgroundChild != null && mShelf.getVisibility() != GONE) {
shelfHeight = mShelf.getIntrinsicHeight();
}
return mIntrinsicPadding + firstChildMinHeight + shelfHeight;
}
private int clampPadding(int desiredPadding) {
return Math.max(desiredPadding, mIntrinsicPadding);
}
private float getRubberBandFactor(boolean onTop) {
if (!onTop) {
return RUBBER_BAND_FACTOR_NORMAL;
}
if (mExpandedInThisMotion) {
return RUBBER_BAND_FACTOR_AFTER_EXPAND;
} else if (mIsExpansionChanging || mPanelTracking) {
return RUBBER_BAND_FACTOR_ON_PANEL_EXPAND;
} else if (mScrolledToTopOnFirstDown) {
return 1.0f;
}
return RUBBER_BAND_FACTOR_NORMAL;
}
private boolean isRubberbanded(boolean onTop) {
return !onTop || mExpandedInThisMotion || mIsExpansionChanging || mPanelTracking
|| !mScrolledToTopOnFirstDown;
}
private void endDrag() {
setIsBeingDragged(false);
recycleVelocityTracker();
if (getCurrentOverScrollAmount(true ) > 0) {
setOverScrollAmount(0, true , true );
}
if (getCurrentOverScrollAmount(false ) > 0) {
setOverScrollAmount(0, false , true );
}
}
private void transformTouchEvent(MotionEvent ev, View sourceView, View targetView) {
ev.offsetLocation(sourceView.getX(), sourceView.getY());
ev.offsetLocation(-targetView.getX(), -targetView.getY());
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
initDownStates(ev);
handleEmptySpaceClick(ev);
boolean expandWantsIt = false;
if (!mSwipingInProgress && !mOnlyScrollingInThisMotion) {
expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
}
boolean scrollWantsIt = false;
if (!mSwipingInProgress && !mExpandingNotification) {
scrollWantsIt = onInterceptTouchEventScroll(ev);
}
boolean swipeWantsIt = false;
if (!mIsBeingDragged
&& !mExpandingNotification
&& !mExpandedInThisMotion
&& !mOnlyScrollingInThisMotion
&& !mDisallowDismissInThisMotion) {
swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
}
boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
if (!isTouchInView(ev, guts) && isUp && !swipeWantsIt && !expandWantsIt
&& !scrollWantsIt) {
mCheckForLeavebehind = false;
mStatusBar.getGutsManager().closeAndSaveGuts(true ,
false , false , -1 , -1 ,
false );
}
if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
mCheckForLeavebehind = true;
}
return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
}
private void handleEmptySpaceClick(MotionEvent ev) {
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_MOVE:
if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > mTouchSlop
|| Math.abs(ev.getX() - mInitialTouchX) > mTouchSlop )) {
mTouchIsClick = false;
}
break;
case MotionEvent.ACTION_UP:
if (mStatusBarState != StatusBarState.KEYGUARD && mTouchIsClick &&
isBelowLastNotification(mInitialTouchX, mInitialTouchY)) {
mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY);
}
break;
}
}
private void initDownStates(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
mExpandedInThisMotion = false;
mOnlyScrollingInThisMotion = !mScroller.isFinished();
mDisallowScrollingInThisMotion = false;
mDisallowDismissInThisMotion = false;
mTouchIsClick = true;
mInitialTouchX = ev.getX();
mInitialTouchY = ev.getY();
}
}
public void setChildTransferInProgress(boolean childTransferInProgress) {
mChildTransferInProgress = childTransferInProgress;
}
@Override
public void onViewRemoved(View child) {
super.onViewRemoved(child);
if (!mChildTransferInProgress) {
onViewRemovedInternal(child, this);
}
}
@Override
public void cleanUpViewState(View child) {
if (child == mTranslatingParentView) {
mTranslatingParentView = null;
}
mCurrentStackScrollState.removeViewStateForView(child);
}
@Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
super.requestDisallowInterceptTouchEvent(disallowIntercept);
if (disallowIntercept) {
cancelLongPress();
}
}
private void onViewRemovedInternal(View child, ViewGroup container) {
if (mChangePositionInProgress) {
return;
}
ExpandableView expandableView = (ExpandableView) child;
expandableView.setOnHeightChangedListener(null);
mCurrentStackScrollState.removeViewStateForView(child);
updateScrollStateForRemovedChild(expandableView);
boolean animationGenerated = generateRemoveAnimation(child);
if (animationGenerated) {
if (!mSwipedOutViews.contains(child)
|| Math.abs(expandableView.getTranslation()) != expandableView.getWidth()) {
container.addTransientView(child, 0);
expandableView.setTransientContainer(container);
}
} else {
mSwipedOutViews.remove(child);
}
updateAnimationState(false, child);
focusNextViewIfFocused(child);
}
private void focusNextViewIfFocused(View view) {
if (view instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
if (row.shouldRefocusOnDismiss()) {
View nextView = row.getChildAfterViewWhenDismissed();
if (nextView == null) {
View groupParentWhenDismissed = row.getGroupParentWhenDismissed();
nextView = getFirstChildBelowTranlsationY(groupParentWhenDismissed != null
? groupParentWhenDismissed.getTranslationY()
: view.getTranslationY(), true );
}
if (nextView != null) {
nextView.requestAccessibilityFocus();
}
}
}
}
private boolean isChildInGroup(View child) {
return child instanceof ExpandableNotificationRow
&& mGroupManager.isChildInGroupWithSummary(
((ExpandableNotificationRow) child).getStatusBarNotification());
}
private boolean generateRemoveAnimation(View child) {
if (removeRemovedChildFromHeadsUpChangeAnimations(child)) {
mAddedHeadsUpChildren.remove(child);
return false;
}
if (isClickedHeadsUp(child)) {
mClearTransientViewsWhenFinished.add((ExpandableView) child);
return true;
}
if (mIsExpanded && mAnimationsEnabled && !isChildInInvisibleGroup(child)) {
if (!mChildrenToAddAnimated.contains(child)) {
mChildrenToRemoveAnimated.add(child);
mNeedsAnimation = true;
return true;
} else {
mChildrenToAddAnimated.remove(child);
mFromMoreCardAdditions.remove(child);
return false;
}
}
return false;
}
private boolean isClickedHeadsUp(View child) {
return HeadsUpUtil.isClickedHeadsUpNotification(child);
}
private boolean removeRemovedChildFromHeadsUpChangeAnimations(View child) {
boolean hasAddEvent = false;
for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
ExpandableNotificationRow row = eventPair.first;
boolean isHeadsUp = eventPair.second;
if (child == row) {
mTmpList.add(eventPair);
hasAddEvent |= isHeadsUp;
}
}
if (hasAddEvent) {
mHeadsUpChangeAnimations.removeAll(mTmpList);
((ExpandableNotificationRow ) child).setHeadsUpAnimatingAway(false);
}
mTmpList.clear();
return hasAddEvent;
}
private boolean isChildInInvisibleGroup(View child) {
if (child instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
ExpandableNotificationRow groupSummary =
mGroupManager.getGroupSummary(row.getStatusBarNotification());
if (groupSummary != null && groupSummary != row) {
return row.getVisibility() == View.INVISIBLE;
}
}
return false;
}
private void updateScrollStateForRemovedChild(ExpandableView removedChild) {
int startingPosition = getPositionInLinearLayout(removedChild);
float increasedPaddingAmount = removedChild.getIncreasedPaddingAmount();
int padding;
if (increasedPaddingAmount >= 0) {
padding = (int) NotificationUtils.interpolate(
mPaddingBetweenElements,
mIncreasedPaddingBetweenElements,
increasedPaddingAmount);
} else {
padding = (int) NotificationUtils.interpolate(
0,
mPaddingBetweenElements,
1.0f + increasedPaddingAmount);
}
int childHeight = getIntrinsicHeight(removedChild) + padding;
int endPosition = startingPosition + childHeight;
if (endPosition <= mOwnScrollY) {
setOwnScrollY(mOwnScrollY - childHeight);
} else if (startingPosition < mOwnScrollY) {
setOwnScrollY(startingPosition);
}
}
private int getIntrinsicHeight(View view) {
if (view instanceof ExpandableView) {
ExpandableView expandableView = (ExpandableView) view;
return expandableView.getIntrinsicHeight();
}
return view.getHeight();
}
public int getPositionInLinearLayout(View requestedView) {
ExpandableNotificationRow childInGroup = null;
ExpandableNotificationRow requestedRow = null;
if (isChildInGroup(requestedView)) {
childInGroup = (ExpandableNotificationRow) requestedView;
requestedView = requestedRow = childInGroup.getNotificationParent();
}
int position = 0;
float previousPaddingRequest = mPaddingBetweenElements;
float previousPaddingAmount = 0.0f;
for (int i = 0; i < getChildCount(); i++) {
ExpandableView child = (ExpandableView) getChildAt(i);
boolean notGone = child.getVisibility() != View.GONE;
if (notGone && !child.hasNoContentHeight()) {
float increasedPaddingAmount = child.getIncreasedPaddingAmount();
float padding;
if (increasedPaddingAmount >= 0.0f) {
padding = (int) NotificationUtils.interpolate(
previousPaddingRequest,
mIncreasedPaddingBetweenElements,
increasedPaddingAmount);
previousPaddingRequest = (int) NotificationUtils.interpolate(
mPaddingBetweenElements,
mIncreasedPaddingBetweenElements,
increasedPaddingAmount);
} else {
int ownPadding = (int) NotificationUtils.interpolate(
0,
mPaddingBetweenElements,
1.0f + increasedPaddingAmount);
if (previousPaddingAmount > 0.0f) {
padding = (int) NotificationUtils.interpolate(
ownPadding,
mIncreasedPaddingBetweenElements,
previousPaddingAmount);
} else {
padding = ownPadding;
}
previousPaddingRequest = ownPadding;
}
if (position != 0) {
position += padding;
}
previousPaddingAmount = increasedPaddingAmount;
}
if (child == requestedView) {
if (requestedRow != null) {
position += requestedRow.getPositionOfChild(childInGroup);
}
return position;
}
if (notGone) {
position += getIntrinsicHeight(child);
}
}
return 0;
}
@Override
public void onViewAdded(View child) {
super.onViewAdded(child);
onViewAddedInternal(child);
}
private void updateFirstAndLastBackgroundViews() {
ActivatableNotificationView firstChild = getFirstChildWithBackground();
ActivatableNotificationView lastChild = getLastChildWithBackground();
if (mAnimationsEnabled && mIsExpanded) {
mAnimateNextBackgroundTop = firstChild != mFirstVisibleBackgroundChild;
mAnimateNextBackgroundBottom = lastChild != mLastVisibleBackgroundChild;
} else {
mAnimateNextBackgroundTop = false;
mAnimateNextBackgroundBottom = false;
}
mFirstVisibleBackgroundChild = firstChild;
mLastVisibleBackgroundChild = lastChild;
mAmbientState.setLastVisibleBackgroundChild(lastChild);
mRoundnessManager.setFirstAndLastBackgroundChild(mFirstVisibleBackgroundChild,
mLastVisibleBackgroundChild);
invalidate();
}
private void onViewAddedInternal(View child) {
updateHideSensitiveForChild(child);
((ExpandableView) child).setOnHeightChangedListener(this);
generateAddAnimation(child, false );
updateAnimationState(child);
updateChronometerForChild(child);
}
private void updateHideSensitiveForChild(View child) {
if (child instanceof ExpandableView) {
ExpandableView expandableView = (ExpandableView) child;
expandableView.setHideSensitiveForIntrinsicHeight(mAmbientState.isHideSensitive());
}
}
@Override
public void notifyGroupChildRemoved(View row, ViewGroup childrenContainer) {
onViewRemovedInternal(row, childrenContainer);
}
@Override
public void notifyGroupChildAdded(View row) {
onViewAddedInternal(row);
}
public void setAnimationsEnabled(boolean animationsEnabled) {
mAnimationsEnabled = animationsEnabled;
updateNotificationAnimationStates();
if (!animationsEnabled) {
mSwipedOutViews.clear();
mChildrenToRemoveAnimated.clear();
clearTemporaryViewsInGroup(this);
}
}
private void updateNotificationAnimationStates() {
boolean running = mAnimationsEnabled || hasPulsingNotifications();
mShelf.setAnimationsEnabled(running);
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
running &= mIsExpanded || isPinnedHeadsUp(child);
updateAnimationState(running, child);
}
}
private void updateAnimationState(View child) {
updateAnimationState((mAnimationsEnabled || hasPulsingNotifications())
&& (mIsExpanded || isPinnedHeadsUp(child)), child);
}
@Override
public void setExpandingNotification(ExpandableNotificationRow row) {
mAmbientState.setExpandingNotification(row);
requestChildrenUpdate();
}
@Override
public void bindRow(ExpandableNotificationRow row) {
row.setHeadsUpAnimatingAwayListener(animatingAway -> {
mRoundnessManager.onHeadsupAnimatingAwayChanged(row, animatingAway);
mHeadsUpAppearanceController.updateHeader(row.getEntry());
});
}
@Override
public void applyExpandAnimationParams(ExpandAnimationParameters params) {
mAmbientState.setExpandAnimationTopChange(params == null ? 0 : params.getTopChange());
requestChildrenUpdate();
}
private void updateAnimationState(boolean running, View child) {
if (child instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
row.setIconAnimationRunning(running);
}
}
public boolean isAddOrRemoveAnimationPending() {
return mNeedsAnimation
&& (!mChildrenToAddAnimated.isEmpty() || !mChildrenToRemoveAnimated.isEmpty());
}
@Override
public void generateAddAnimation(View child, boolean fromMoreCard) {
if (mIsExpanded && mAnimationsEnabled && !mChangePositionInProgress) {
mChildrenToAddAnimated.add(child);
if (fromMoreCard) {
mFromMoreCardAdditions.add(child);
}
mNeedsAnimation = true;
}
if (isHeadsUp(child) && mAnimationsEnabled && !mChangePositionInProgress) {
mAddedHeadsUpChildren.add(child);
mChildrenToAddAnimated.remove(child);
}
}
@Override
public void changeViewPosition(View child, int newIndex) {
int currentIndex = indexOfChild(child);
if (currentIndex == -1) {
boolean isTransient = false;
if (child instanceof ExpandableNotificationRow
&& ((ExpandableNotificationRow)child).getTransientContainer() != null) {
isTransient = true;
}
Log.e(TAG, "Attempting to re-position "
+ (isTransient ? "transient" : "")
+ " view {"
+ child
+ "}");
return;
}
if (child != null && child.getParent() == this && currentIndex != newIndex) {
mChangePositionInProgress = true;
((ExpandableView)child).setChangingPosition(true);
removeView(child);
addView(child, newIndex);
((ExpandableView)child).setChangingPosition(false);
mChangePositionInProgress = false;
if (mIsExpanded && mAnimationsEnabled && child.getVisibility() != View.GONE) {
mChildrenChangingPositions.add(child);
mNeedsAnimation = true;
}
}
}
private void startAnimationToState() {
if (mNeedsAnimation) {
generateChildHierarchyEvents();
mNeedsAnimation = false;
}
if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) {
setAnimationRunning(true);
mStateAnimator.startAnimationForEvents(mAnimationEvents, mCurrentStackScrollState,
mGoToFullShadeDelay);
mAnimationEvents.clear();
updateBackground();
updateViewShadows();
updateClippingToTopRoundedCorner();
} else {
applyCurrentState();
}
mGoToFullShadeDelay = 0;
}
private void generateChildHierarchyEvents() {
generateHeadsUpAnimationEvents();
generateChildRemovalEvents();
generateChildAdditionEvents();
generatePositionChangeEvents();
generateSnapBackEvents();
generateDragEvents();
generateTopPaddingEvent();
generateActivateEvent();
generateDimmedEvent();
generateHideSensitiveEvent();
generateDarkEvent();
generateGoToFullShadeEvent();
generateViewResizeEvent();
generateGroupExpansionEvent();
generateAnimateEverythingEvent();
generatePulsingAnimationEvent();
mNeedsAnimation = false;
}
private void generateHeadsUpAnimationEvents() {
for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
ExpandableNotificationRow row = eventPair.first;
boolean isHeadsUp = eventPair.second;
int type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_OTHER;
boolean onBottom = false;
boolean pinnedAndClosed = row.isPinned() && !mIsExpanded;
if (!mIsExpanded && !isHeadsUp) {
type = row.wasJustClicked()
? AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
: AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR;
if (row.isChildInGroup()) {
row.setHeadsUpAnimatingAway(false);
continue;
}
} else {
ExpandableViewState viewState = mCurrentStackScrollState.getViewStateForView(row);
if (viewState == null) {
continue;
}
if (isHeadsUp && (mAddedHeadsUpChildren.contains(row) || pinnedAndClosed)) {
if (pinnedAndClosed || shouldHunAppearFromBottom(viewState)) {
type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR;
} else {
type = AnimationEvent.ANIMATION_TYPE_ADD;
}
onBottom = !pinnedAndClosed;
}
}
AnimationEvent event = new AnimationEvent(row, type);
event.headsUpFromBottom = onBottom;
mAnimationEvents.add(event);
}
mHeadsUpChangeAnimations.clear();
mAddedHeadsUpChildren.clear();
}
private boolean shouldHunAppearFromBottom(ExpandableViewState viewState) {
if (viewState.yTranslation + viewState.height < mAmbientState.getMaxHeadsUpTranslation()) {
return false;
}
return true;
}
private void generateGroupExpansionEvent() {
if (mExpandedGroupView != null) {
mAnimationEvents.add(new AnimationEvent(mExpandedGroupView,
AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED));
mExpandedGroupView = null;
}
}
private void generateViewResizeEvent() {
if (mNeedViewResizeAnimation) {
mAnimationEvents.add(
new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_VIEW_RESIZE));
}
mNeedViewResizeAnimation = false;
}
private void generateSnapBackEvents() {
for (View child : mSnappedBackChildren) {
mAnimationEvents.add(new AnimationEvent(child,
AnimationEvent.ANIMATION_TYPE_SNAP_BACK));
}
mSnappedBackChildren.clear();
}
private void generateDragEvents() {
for (View child : mDragAnimPendingChildren) {
mAnimationEvents.add(new AnimationEvent(child,
AnimationEvent.ANIMATION_TYPE_START_DRAG));
}
mDragAnimPendingChildren.clear();
}
private void generateChildRemovalEvents() {
for (View child : mChildrenToRemoveAnimated) {
boolean childWasSwipedOut = mSwipedOutViews.contains(child);
float removedTranslation = child.getTranslationY();
boolean ignoreChildren = true;
if (child instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
if (row.isRemoved() && row.wasChildInGroupWhenRemoved()) {
removedTranslation = row.getTranslationWhenRemoved();
ignoreChildren = false;
}
childWasSwipedOut |= Math.abs(row.getTranslation()) == row.getWidth();
}
if (!childWasSwipedOut) {
Rect clipBounds = child.getClipBounds();
childWasSwipedOut = clipBounds != null && clipBounds.height() == 0;
if (childWasSwipedOut && child instanceof ExpandableView) {
ViewGroup transientContainer = ((ExpandableView) child).getTransientContainer();
if (transientContainer != null) {
transientContainer.removeTransientView(child);
}
}
}
int animationType = childWasSwipedOut
? AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT
: AnimationEvent.ANIMATION_TYPE_REMOVE;
AnimationEvent event = new AnimationEvent(child, animationType);
event.viewAfterChangingView = getFirstChildBelowTranlsationY(removedTranslation,
ignoreChildren);
mAnimationEvents.add(event);
mSwipedOutViews.remove(child);
}
mChildrenToRemoveAnimated.clear();
}
private void generatePositionChangeEvents() {
for (View child : mChildrenChangingPositions) {
mAnimationEvents.add(new AnimationEvent(child,
AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
}
mChildrenChangingPositions.clear();
if (mGenerateChildOrderChangedEvent) {
mAnimationEvents.add(new AnimationEvent(null,
AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
mGenerateChildOrderChangedEvent = false;
}
}
private void generateChildAdditionEvents() {
for (View child : mChildrenToAddAnimated) {
if (mFromMoreCardAdditions.contains(child)) {
mAnimationEvents.add(new AnimationEvent(child,
AnimationEvent.ANIMATION_TYPE_ADD,
StackStateAnimator.ANIMATION_DURATION_STANDARD));
} else {
mAnimationEvents.add(new AnimationEvent(child,
AnimationEvent.ANIMATION_TYPE_ADD));
}
}
mChildrenToAddAnimated.clear();
mFromMoreCardAdditions.clear();
}
private void generateTopPaddingEvent() {
if (mTopPaddingNeedsAnimation) {
AnimationEvent event;
if (mAmbientState.isDark()) {
event = new AnimationEvent(null ,
AnimationEvent.ANIMATION_TYPE_TOP_PADDING_CHANGED,
KeyguardSliceView.DEFAULT_ANIM_DURATION);
} else {
event = new AnimationEvent(null ,
AnimationEvent.ANIMATION_TYPE_TOP_PADDING_CHANGED);
}
mAnimationEvents.add(event);
}
mTopPaddingNeedsAnimation = false;
}
private void generateActivateEvent() {
if (mActivateNeedsAnimation) {
mAnimationEvents.add(
new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_ACTIVATED_CHILD));
}
mActivateNeedsAnimation = false;
}
private void generateAnimateEverythingEvent() {
if (mEverythingNeedsAnimation) {
mAnimationEvents.add(
new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_EVERYTHING));
}
mEverythingNeedsAnimation = false;
}
private void generateDimmedEvent() {
if (mDimmedNeedsAnimation) {
mAnimationEvents.add(
new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_DIMMED));
}
mDimmedNeedsAnimation = false;
}
private void generateHideSensitiveEvent() {
if (mHideSensitiveNeedsAnimation) {
mAnimationEvents.add(
new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_HIDE_SENSITIVE));
}
mHideSensitiveNeedsAnimation = false;
}
private void generateDarkEvent() {
if (mDarkNeedsAnimation) {
AnimationEvent ev = new AnimationEvent(null,
AnimationEvent.ANIMATION_TYPE_DARK,
new AnimationFilter()
.animateDark()
.animateY(mShelf));
ev.darkAnimationOriginIndex = mDarkAnimationOriginIndex;
mAnimationEvents.add(ev);
}
mDarkNeedsAnimation = false;
}
private void generateGoToFullShadeEvent() {
if (mGoToFullShadeNeedsAnimation) {
mAnimationEvents.add(
new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_GO_TO_FULL_SHADE));
}
mGoToFullShadeNeedsAnimation = false;
}
private boolean onInterceptTouchEventScroll(MotionEvent ev) {
if (!isScrollingEnabled()) {
return false;
}
final int action = ev.getAction();
if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
return true;
}
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_MOVE: {
final int activePointerId = mActivePointerId;
if (activePointerId == INVALID_POINTER) {
break;
}
final int pointerIndex = ev.findPointerIndex(activePointerId);
if (pointerIndex == -1) {
Log.e(TAG, "Invalid pointerId=" + activePointerId
+ " in onInterceptTouchEvent");
break;
}
final int y = (int) ev.getY(pointerIndex);
final int x = (int) ev.getX(pointerIndex);
final int yDiff = Math.abs(y - mLastMotionY);
final int xDiff = Math.abs(x - mDownX);
if (yDiff > mTouchSlop && yDiff > xDiff) {
setIsBeingDragged(true);
mLastMotionY = y;
mDownX = x;
initVelocityTrackerIfNotExists();
mVelocityTracker.addMovement(ev);
}
break;
}
case MotionEvent.ACTION_DOWN: {
final int y = (int) ev.getY();
mScrolledToTopOnFirstDown = isScrolledToTop();
if (getChildAtPosition(ev.getX(), y, false ) == null) {
setIsBeingDragged(false);
recycleVelocityTracker();
break;
}
mLastMotionY = y;
mDownX = (int) ev.getX();
mActivePointerId = ev.getPointerId(0);
initOrResetVelocityTracker();
mVelocityTracker.addMovement(ev);
boolean isBeingDragged = !mScroller.isFinished();
setIsBeingDragged(isBeingDragged);
break;
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
setIsBeingDragged(false);
mActivePointerId = INVALID_POINTER;
recycleVelocityTracker();
if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
animateScroll();
}
break;
case MotionEvent.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
break;
}
return mIsBeingDragged;
}
protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
return new StackScrollAlgorithm(context);
}
private boolean isInContentBounds(MotionEvent event) {
return isInContentBounds(event.getY());
}
public boolean isInContentBounds(float y) {
return y < getHeight() - getEmptyBottomMargin();
}
private void setIsBeingDragged(boolean isDragged) {
mIsBeingDragged = isDragged;
if (isDragged) {
requestDisallowInterceptTouchEvent(true);
cancelLongPress();
}
}
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
if (!hasWindowFocus) {
cancelLongPress();
}
}
@Override
public void clearChildFocus(View child) {
super.clearChildFocus(child);
if (mForcedScroll == child) {
mForcedScroll = null;
}
}
public void requestDisallowLongPress() {
cancelLongPress();
}
public void requestDisallowDismiss() {
mDisallowDismissInThisMotion = true;
}
public void cancelLongPress() {
mSwipeHelper.cancelLongPress();
}
@Override
public boolean isScrolledToTop() {
return mOwnScrollY == 0;
}
@Override
public boolean isScrolledToBottom() {
return mOwnScrollY >= getScrollRange();
}
@Override
public View getHostView() {
return this;
}
public int getEmptyBottomMargin() {
return Math.max(mMaxLayoutHeight - mContentHeight, 0);
}
public void checkSnoozeLeavebehind() {
if (mCheckForLeavebehind) {
mStatusBar.getGutsManager().closeAndSaveGuts(true ,
false , false , -1 , -1 ,
false );
mCheckForLeavebehind = false;
}
}
public void resetCheckSnoozeLeavebehind() {
mCheckForLeavebehind = true;
}
public void onExpansionStarted() {
mIsExpansionChanging = true;
mAmbientState.setExpansionChanging(true);
checkSnoozeLeavebehind();
}
public void onExpansionStopped() {
mIsExpansionChanging = false;
resetCheckSnoozeLeavebehind();
mAmbientState.setExpansionChanging(false);
if (!mIsExpanded) {
setOwnScrollY(0);
mStatusBar.resetUserExpandedStates();
clearTemporaryViews();
clearUserLockedViews();
}
}
private void clearUserLockedViews() {
for (int i = 0; i < getChildCount(); i++) {
ExpandableView child = (ExpandableView) getChildAt(i);
if (child instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
row.setUserLocked(false);
}
}
}
private void clearTemporaryViews() {
clearTemporaryViewsInGroup(this);
for (int i = 0; i < getChildCount(); i++) {
ExpandableView child = (ExpandableView) getChildAt(i);
if (child instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
clearTemporaryViewsInGroup(row.getChildrenContainer());
}
}
}
private void clearTemporaryViewsInGroup(ViewGroup viewGroup) {
while (viewGroup != null && viewGroup.getTransientViewCount() != 0) {
viewGroup.removeTransientView(viewGroup.getTransientView(0));
}
}
public void onPanelTrackingStarted() {
mPanelTracking = true;
mAmbientState.setPanelTracking(true);
}
public void onPanelTrackingStopped() {
mPanelTracking = false;
mAmbientState.setPanelTracking(false);
}
public void resetScrollPosition() {
mScroller.abortAnimation();
setOwnScrollY(0);
}
private void setIsExpanded(boolean isExpanded) {
boolean changed = isExpanded != mIsExpanded;
mIsExpanded = isExpanded;
mStackScrollAlgorithm.setIsExpanded(isExpanded);
if (changed) {
if (!mIsExpanded) {
mGroupManager.collapseAllGroups();
mExpandHelper.cancelImmediately();
}
updateNotificationAnimationStates();
updateChronometers();
requestChildrenUpdate();
}
}
private void updateChronometers() {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
updateChronometerForChild(getChildAt(i));
}
}
private void updateChronometerForChild(View child) {
if (child instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
row.setChronometerRunning(mIsExpanded);
}
}
@Override
public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
updateContentHeight();
updateScrollPositionOnExpandInBottom(view);
clampScrollPosition();
notifyHeightChangeListener(view, needsAnimation);
ExpandableNotificationRow row = view instanceof ExpandableNotificationRow
? (ExpandableNotificationRow) view
: null;
if (row != null && (row == mFirstVisibleBackgroundChild
|| row.getNotificationParent() == mFirstVisibleBackgroundChild)) {
updateAlgorithmLayoutMinHeight();
}
if (needsAnimation) {
requestAnimationOnViewResize(row);
}
requestChildrenUpdate();
}
@Override
public void onReset(ExpandableView view) {
updateAnimationState(view);
updateChronometerForChild(view);
}
private void updateScrollPositionOnExpandInBottom(ExpandableView view) {
if (view instanceof ExpandableNotificationRow && !onKeyguard()) {
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
if (row.isUserLocked() && row != getFirstChildNotGone()) {
if (row.isSummaryWithChildren()) {
return;
}
float endPosition = row.getTranslationY() + row.getActualHeight();
if (row.isChildInGroup()) {
endPosition += row.getNotificationParent().getTranslationY();
}
int layoutEnd = mMaxLayoutHeight + (int) mStackTranslation;
if (row != mLastVisibleBackgroundChild && mShelf.getVisibility() != GONE) {
layoutEnd -= mShelf.getIntrinsicHeight() + mPaddingBetweenElements;
}
if (endPosition > layoutEnd) {
setOwnScrollY((int) (mOwnScrollY + endPosition - layoutEnd));
mDisallowScrollingInThisMotion = true;
}
}
}
}
public void setOnHeightChangedListener(
ExpandableView.OnHeightChangedListener mOnHeightChangedListener) {
this.mOnHeightChangedListener = mOnHeightChangedListener;
}
public void setOnEmptySpaceClickListener(OnEmptySpaceClickListener listener) {
mOnEmptySpaceClickListener = listener;
}
public void onChildAnimationFinished() {
setAnimationRunning(false);
requestChildrenUpdate();
runAnimationFinishedRunnables();
clearTransient();
clearHeadsUpDisappearRunning();
}
private void clearHeadsUpDisappearRunning() {
for (int i = 0; i < getChildCount(); i++) {
View view = getChildAt(i);
if (view instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
row.setHeadsUpAnimatingAway(false);
if (row.isSummaryWithChildren()) {
for (ExpandableNotificationRow child : row.getNotificationChildren()) {
child.setHeadsUpAnimatingAway(false);
}
}
}
}
}
private void clearTransient() {
for (ExpandableView view : mClearTransientViewsWhenFinished) {
StackStateAnimator.removeTransientView(view);
}
mClearTransientViewsWhenFinished.clear();
}
private void runAnimationFinishedRunnables() {
for (Runnable runnable : mAnimationFinishedRunnables) {
runnable.run();
}
mAnimationFinishedRunnables.clear();
}
public void setDimmed(boolean dimmed, boolean animate) {
dimmed &= onKeyguard();
mAmbientState.setDimmed(dimmed);
if (animate && mAnimationsEnabled) {
mDimmedNeedsAnimation = true;
mNeedsAnimation = true;
animateDimmed(dimmed);
} else {
setDimAmount(dimmed ? 1.0f : 0.0f);
}
requestChildrenUpdate();
}
@VisibleForTesting
boolean isDimmed() {
return mAmbientState.isDimmed();
}
private void setDimAmount(float dimAmount) {
mDimAmount = dimAmount;
updateBackgroundDimming();
}
private void animateDimmed(boolean dimmed) {
if (mDimAnimator != null) {
mDimAnimator.cancel();
}
float target = dimmed ? 1.0f : 0.0f;
if (target == mDimAmount) {
return;
}
mDimAnimator = TimeAnimator.ofFloat(mDimAmount, target);
mDimAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED);
mDimAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
mDimAnimator.addListener(mDimEndListener);
mDimAnimator.addUpdateListener(mDimUpdateListener);
mDimAnimator.start();
}
public void setHideSensitive(boolean hideSensitive, boolean animate) {
if (hideSensitive != mAmbientState.isHideSensitive()) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
ExpandableView v = (ExpandableView) getChildAt(i);
v.setHideSensitiveForIntrinsicHeight(hideSensitive);
}
mAmbientState.setHideSensitive(hideSensitive);
if (animate && mAnimationsEnabled) {
mHideSensitiveNeedsAnimation = true;
mNeedsAnimation = true;
}
updateContentHeight();
requestChildrenUpdate();
}
}
public void setActivatedChild(ActivatableNotificationView activatedChild) {
mAmbientState.setActivatedChild(activatedChild);
if (mAnimationsEnabled) {
mActivateNeedsAnimation = true;
mNeedsAnimation = true;
}
requestChildrenUpdate();
}
public ActivatableNotificationView getActivatedChild() {
return mAmbientState.getActivatedChild();
}
private void applyCurrentState() {
mCurrentStackScrollState.apply();
if (mListener != null) {
mListener.onChildLocationsChanged();
}
runAnimationFinishedRunnables();
setAnimationRunning(false);
updateBackground();
updateViewShadows();
updateClippingToTopRoundedCorner();
}
private void updateViewShadows() {
for (int i = 0; i < getChildCount(); i++) {
ExpandableView child = (ExpandableView) getChildAt(i);
if (child.getVisibility() != GONE) {
mTmpSortedChildren.add(child);
}
}
Collections.sort(mTmpSortedChildren, mViewPositionComparator);
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) {
expandableView.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
} else {
float yLocation = previous.getTranslationY() + previous.getActualHeight() -
expandableView.getTranslationY() - previous.getExtraBottomPadding();
expandableView.setFakeShadowIntensity(
diff / FakeShadowView.SHADOW_SIBLING_TRESHOLD,
previous.getOutlineAlpha(), (int) yLocation,
previous.getOutlineTranslation());
}
previous = expandableView;
}
mTmpSortedChildren.clear();
}
public void updateDecorViews(boolean lightTheme) {
if (lightTheme == mUsingLightTheme) {
return;
}
mUsingLightTheme = lightTheme;
Context context = new ContextThemeWrapper(mContext,
lightTheme ? R.style.Theme_SystemUI_Light : R.style.Theme_SystemUI);
final int textColor = Utils.getColorAttr(context, R.attr.wallpaperTextColor);
mFooterView.setTextColor(textColor);
mEmptyShadeView.setTextColor(textColor);
}
public void goToFullShade(long delay) {
mGoToFullShadeNeedsAnimation = true;
mGoToFullShadeDelay = delay;
mNeedsAnimation = true;
requestChildrenUpdate();
}
public void cancelExpandHelper() {
mExpandHelper.cancel();
}
public void setIntrinsicPadding(int intrinsicPadding) {
mIntrinsicPadding = intrinsicPadding;
mAmbientState.setIntrinsicPadding(intrinsicPadding);
}
public int getIntrinsicPadding() {
return mIntrinsicPadding;
}
public float getNotificationsTopY() {
return mTopPadding + getStackTranslation();
}
@Override
public boolean shouldDelayChildPressedState() {
return true;
}
public void setDark(boolean dark, boolean animate, @Nullable PointF touchWakeUpScreenLocation) {
if (mAmbientState.isDark() == dark) {
return;
}
mAmbientState.setDark(dark);
if (animate && mAnimationsEnabled) {
mDarkNeedsAnimation = true;
mDarkAnimationOriginIndex = findDarkAnimationOriginIndex(touchWakeUpScreenLocation);
mNeedsAnimation = true;
} else {
setDarkAmount(dark ? 1f : 0f);
updateBackground();
}
requestChildrenUpdate();
applyCurrentBackgroundBounds();
updateWillNotDraw();
notifyHeightChangeListener(mShelf);
}
private void updatePanelTranslation() {
setTranslationX(mVerticalPanelTranslation + mAntiBurnInOffsetX * mInterpolatedDarkAmount);
}
public void setVerticalPanelTranslation(float verticalPanelTranslation) {
mVerticalPanelTranslation = verticalPanelTranslation;
updatePanelTranslation();
}
private void updateWillNotDraw() {
boolean willDraw = mShouldDrawNotificationBackground || DEBUG;
setWillNotDraw(!willDraw);
}
private void setDarkAmount(float darkAmount) {
setDarkAmount(darkAmount, darkAmount);
}
public void setDarkAmount(float linearDarkAmount, float interpolatedDarkAmount) {
mLinearDarkAmount = linearDarkAmount;
mInterpolatedDarkAmount = interpolatedDarkAmount;
boolean wasFullyDark = mAmbientState.isFullyDark();
mAmbientState.setDarkAmount(interpolatedDarkAmount);
boolean nowFullyDark = mAmbientState.isFullyDark();
if (nowFullyDark != wasFullyDark) {
updateContentHeight();
DozeParameters dozeParameters = DozeParameters.getInstance(mContext);
if (nowFullyDark && dozeParameters.shouldControlScreenOff()) {
mShelf.fadeInTranslating();
}
if (mIconAreaController != null) {
mIconAreaController.setFullyDark(nowFullyDark);
}
}
updateAlgorithmHeightAndPadding();
updateBackgroundDimming();
updatePanelTranslation();
requestChildrenUpdate();
}
public void notifyDarkAnimationStart(boolean dark) {
if (mInterpolatedDarkAmount == 0 || mInterpolatedDarkAmount == 1) {
mBackgroundXFactor = dark ? 1.8f : 1.5f;
mDarkXInterpolator = dark
? Interpolators.FAST_OUT_SLOW_IN_REVERSE
: Interpolators.FAST_OUT_SLOW_IN;
}
}
public long getDarkAnimationDuration(boolean dark) {
long duration = StackStateAnimator.ANIMATION_DURATION_WAKEUP;
if (dark && getNotGoneChildCount() > 2) {
duration *= 1.2f;
}
return duration;
}
private int findDarkAnimationOriginIndex(@Nullable PointF screenLocation) {
if (screenLocation == null || screenLocation.y < mTopPadding) {
return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
}
if (screenLocation.y > getBottomMostNotificationBottom()) {
return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_BELOW;
}
View child = getClosestChildAtRawPosition(screenLocation.x, screenLocation.y);
if (child != null) {
return getNotGoneIndex(child);
} else {
return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
}
}
private int getNotGoneIndex(View child) {
int count = getChildCount();
int notGoneIndex = 0;
for (int i = 0; i < count; i++) {
View v = getChildAt(i);
if (child == v) {
return notGoneIndex;
}
if (v.getVisibility() != View.GONE) {
notGoneIndex++;
}
}
return -1;
}
public void setFooterView(@NonNull FooterView footerView) {
int index = -1;
if (mFooterView != null) {
index = indexOfChild(mFooterView);
removeView(mFooterView);
}
mFooterView = footerView;
addView(mFooterView, index);
}
public void setEmptyShadeView(EmptyShadeView emptyShadeView) {
int index = -1;
if (mEmptyShadeView != null) {
index = indexOfChild(mEmptyShadeView);
removeView(mEmptyShadeView);
}
mEmptyShadeView = emptyShadeView;
addView(mEmptyShadeView, index);
}
public void updateEmptyShadeView(boolean visible) {
mEmptyShadeView.setVisible(visible, mIsExpanded && mAnimationsEnabled);
int oldTextRes = mEmptyShadeView.getTextResource();
int newTextRes = mStatusBar.areNotificationsHidden()
? R.string.dnd_suppressing_shade_text : R.string.empty_shade_text;
if (oldTextRes != newTextRes) {
mEmptyShadeView.setText(newTextRes);
}
}
public void updateFooterView(boolean visible, boolean showDismissView) {
if (mFooterView == null) {
return;
}
boolean animate = mIsExpanded && mAnimationsEnabled;
mFooterView.setVisible(visible, animate);
mFooterView.setSecondaryVisible(showDismissView, animate);
}
public void setDismissAllInProgress(boolean dismissAllInProgress) {
mDismissAllInProgress = dismissAllInProgress;
mAmbientState.setDismissAllInProgress(dismissAllInProgress);
handleDismissAllClipping();
}
private void handleDismissAllClipping() {
final int count = getChildCount();
boolean previousChildWillBeDismissed = false;
for (int i = 0; i < count; i++) {
ExpandableView child = (ExpandableView) getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
if (mDismissAllInProgress && previousChildWillBeDismissed) {
child.setMinClipTopAmount(child.getClipTopAmount());
} else {
child.setMinClipTopAmount(0);
}
previousChildWillBeDismissed = canChildBeDismissed(child);
}
}
public boolean isFooterViewNotGone() {
return mFooterView != null
&& mFooterView.getVisibility() != View.GONE
&& !mFooterView.willBeGone();
}
public boolean isFooterViewContentVisible() {
return mFooterView != null && mFooterView.isContentVisible();
}
public int getFooterViewHeight() {
return mFooterView == null ? 0 : mFooterView.getHeight() + mPaddingBetweenElements;
}
public int getEmptyShadeViewHeight() {
return mEmptyShadeView.getHeight();
}
public float getBottomMostNotificationBottom() {
final int count = getChildCount();
float max = 0;
for (int childIdx = 0; childIdx < count; childIdx++) {
ExpandableView child = (ExpandableView) getChildAt(childIdx);
if (child.getVisibility() == GONE) {
continue;
}
float bottom = child.getTranslationY() + child.getActualHeight()
- child.getClipBottomAmount();
if (bottom > max) {
max = bottom;
}
}
return max + getStackTranslation();
}
public void setStatusBar(StatusBar statusBar) {
this.mStatusBar = statusBar;
}
public void setGroupManager(NotificationGroupManager groupManager) {
this.mGroupManager = groupManager;
}
public void onGoToKeyguard() {
requestAnimateEverything();
}
private void requestAnimateEverything() {
if (mIsExpanded && mAnimationsEnabled) {
mEverythingNeedsAnimation = true;
mNeedsAnimation = true;
requestChildrenUpdate();
}
}
public boolean isBelowLastNotification(float touchX, float touchY) {
int childCount = getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
ExpandableView child = (ExpandableView) getChildAt(i);
if (child.getVisibility() != View.GONE) {
float childTop = child.getY();
if (childTop > touchY) {
return false;
}
boolean belowChild = touchY > childTop + child.getActualHeight()
- child.getClipBottomAmount();
if (child == mFooterView) {
if(!belowChild && !mFooterView.isOnEmptySpace(touchX - mFooterView.getX(),
touchY - childTop)) {
return false;
}
} else if (child == mEmptyShadeView) {
return true;
} else if (!belowChild){
return false;
}
}
}
return touchY > mTopPadding + mStackTranslation;
}
@Override
public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) {
boolean animated = !mGroupExpandedForMeasure && mAnimationsEnabled
&& (mIsExpanded || changedRow.isPinned());
if (animated) {
mExpandedGroupView = changedRow;
mNeedsAnimation = true;
}
changedRow.setChildrenExpanded(expanded, animated);
if (!mGroupExpandedForMeasure) {
onHeightChanged(changedRow, false );
}
runAfterAnimationFinished(new Runnable() {
@Override
public void run() {
changedRow.onFinishedExpansionChange();
}
});
}
@Override
public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
mStatusBar.requestNotificationUpdate();
}
@Override
public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
super.onInitializeAccessibilityEventInternal(event);
event.setScrollable(mScrollable);
event.setScrollX(mScrollX);
event.setScrollY(mOwnScrollY);
event.setMaxScrollX(mScrollX);
event.setMaxScrollY(getScrollRange());
}
@Override
public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfoInternal(info);
if (mScrollable) {
info.setScrollable(true);
if (mBackwardScrollable) {
info.addAction(
AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP);
}
if (mForwardScrollable) {
info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_DOWN);
}
}
info.setClassName(ScrollView.class.getName());
}
@Override
public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
if (super.performAccessibilityActionInternal(action, arguments)) {
return true;
}
if (!isEnabled()) {
return false;
}
int direction = -1;
switch (action) {
case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
case android.R.id.accessibilityActionScrollDown:
direction = 1;
case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
case android.R.id.accessibilityActionScrollUp:
final int viewportHeight = getHeight() - mPaddingBottom - mTopPadding - mPaddingTop
- mShelf.getIntrinsicHeight();
final int targetScrollY = Math.max(0,
Math.min(mOwnScrollY + direction * viewportHeight, getScrollRange()));
if (targetScrollY != mOwnScrollY) {
mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScrollY - mOwnScrollY);
animateScroll();
return true;
}
break;
}
return false;
}
@Override
public void onGroupsChanged() {
mStatusBar.requestNotificationUpdate();
}
public void generateChildOrderChangedEvent() {
if (mIsExpanded && mAnimationsEnabled) {
mGenerateChildOrderChangedEvent = true;
mNeedsAnimation = true;
requestChildrenUpdate();
}
}
@Override
public int getContainerChildCount() {
return getChildCount();
}
@Override
public View getContainerChildAt(int i) {
return getChildAt(i);
}
@Override
public void removeContainerView(View v) {
removeView(v);
}
@Override
public void addContainerView(View v) {
addView(v);
}
public void runAfterAnimationFinished(Runnable runnable) {
mAnimationFinishedRunnables.add(runnable);
}
public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
mHeadsUpManager = headsUpManager;
mAmbientState.setHeadsUpManager(headsUpManager);
mHeadsUpManager.addListener(mRoundnessManager);
}
public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
if (mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed)) {
mHeadsUpChangeAnimations.add(new Pair<>(row, isHeadsUp));
mNeedsAnimation = true;
if (!mIsExpanded && !isHeadsUp) {
row.setHeadsUpAnimatingAway(true);
}
requestChildrenUpdate();
}
}
public void setShadeExpanded(boolean shadeExpanded) {
mAmbientState.setShadeExpanded(shadeExpanded);
mStateAnimator.setShadeExpanded(shadeExpanded);
}
public void setHeadsUpBoundaries(int height, int bottomBarHeight) {
mAmbientState.setMaxHeadsUpTranslation(height - bottomBarHeight);
mStateAnimator.setHeadsUpAppearHeightBottom(height);
requestChildrenUpdate();
}
public void setTrackingHeadsUp(ExpandableNotificationRow row) {
mTrackingHeadsUp = row != null;
mRoundnessManager.setTrackingHeadsUp(row);
}
public void setScrimController(ScrimController scrimController) {
mScrimController = scrimController;
mScrimController.setScrimBehindChangeRunnable(this::updateBackgroundDimming);
}
public void forceNoOverlappingRendering(boolean force) {
mForceNoOverlappingRendering = force;
}
@Override
public boolean hasOverlappingRendering() {
return !mForceNoOverlappingRendering && super.hasOverlappingRendering();
}
public void setAnimationRunning(boolean animationRunning) {
if (animationRunning != mAnimationRunning) {
if (animationRunning) {
getViewTreeObserver().addOnPreDrawListener(mRunningAnimationUpdater);
} else {
getViewTreeObserver().removeOnPreDrawListener(mRunningAnimationUpdater);
}
mAnimationRunning = animationRunning;
updateContinuousShadowDrawing();
}
}
public boolean isExpanded() {
return mIsExpanded;
}
public void setPulsing(boolean pulsing, boolean animated) {
if (!mPulsing && !pulsing) {
return;
}
mPulsing = pulsing;
mNeedingPulseAnimation = animated ? getFirstChildNotGone() : null;
mAmbientState.setPulsing(pulsing);
updateNotificationAnimationStates();
updateAlgorithmHeightAndPadding();
updateContentHeight();
requestChildrenUpdate();
notifyHeightChangeListener(null, animated);
mNeedsAnimation |= animated;
}
private void generatePulsingAnimationEvent() {
if (mNeedingPulseAnimation != null) {
int type = mPulsing ? AnimationEvent.ANIMATION_TYPE_PULSE_APPEAR
: AnimationEvent.ANIMATION_TYPE_PULSE_DISAPPEAR;
mAnimationEvents.add(new AnimationEvent(mNeedingPulseAnimation, type));
mNeedingPulseAnimation = null;
}
}
public void setFadingOut(boolean fadingOut) {
if (fadingOut != mFadingOut) {
mFadingOut = fadingOut;
updateFadingState();
}
}
public void setParentNotFullyVisible(boolean parentNotFullyVisible) {
if (mScrimController == null) {
return;
}
if (parentNotFullyVisible != mParentNotFullyVisible) {
mParentNotFullyVisible = parentNotFullyVisible;
updateFadingState();
}
}
private void updateFadingState() {
applyCurrentBackgroundBounds();
updateSrcDrawing();
}
@Override
public void setAlpha(@FloatRange(from = 0.0, to = 1.0) float alpha) {
super.setAlpha(alpha);
setFadingOut(alpha != 1.0f);
}
public void setQsExpanded(boolean qsExpanded) {
mQsExpanded = qsExpanded;
updateAlgorithmLayoutMinHeight();
updateScrollability();
}
public void setQsExpansionFraction(float qsExpansionFraction) {
mQsExpansionFraction = qsExpansionFraction;
}
public void setOwnScrollY(int ownScrollY) {
if (ownScrollY != mOwnScrollY) {
onScrollChanged(mScrollX, ownScrollY, mScrollX, mOwnScrollY);
mOwnScrollY = ownScrollY;
updateForwardAndBackwardScrollability();
requestChildrenUpdate();
}
}
public void setShelf(NotificationShelf shelf) {
int index = -1;
if (mShelf != null) {
index = indexOfChild(mShelf);
removeView(mShelf);
}
mShelf = shelf;
addView(mShelf, index);
mAmbientState.setShelf(shelf);
mStateAnimator.setShelf(shelf);
shelf.bind(mAmbientState, this);
}
public NotificationShelf getNotificationShelf() {
return mShelf;
}
public void setMaxDisplayedNotifications(int maxDisplayedNotifications) {
if (mMaxDisplayedNotifications != maxDisplayedNotifications) {
mMaxDisplayedNotifications = maxDisplayedNotifications;
updateContentHeight();
notifyHeightChangeListener(mShelf);
}
}
public void setShouldShowShelfOnly(boolean shouldShowShelfOnly) {
mShouldShowShelfOnly = shouldShowShelfOnly;
updateAlgorithmLayoutMinHeight();
}
public int getMinExpansionHeight() {
return mShelf.getIntrinsicHeight() - (mShelf.getIntrinsicHeight() - mStatusBarHeight) / 2;
}
public void setInHeadsUpPinnedMode(boolean inHeadsUpPinnedMode) {
mInHeadsUpPinnedMode = inHeadsUpPinnedMode;
updateClipping();
}
public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
mHeadsUpAnimatingAway = headsUpAnimatingAway;
updateClipping();
}
public void setStatusBarState(int statusBarState) {
mStatusBarState = statusBarState;
mAmbientState.setStatusBarState(statusBarState);
}
public void setExpandingVelocity(float expandingVelocity) {
mAmbientState.setExpandingVelocity(expandingVelocity);
}
public float getOpeningHeight() {
if (mEmptyShadeView.getVisibility() == GONE) {
return getMinExpansionHeight();
} else {
return getAppearEndPosition();
}
}
public void setIsFullWidth(boolean isFullWidth) {
mAmbientState.setPanelFullWidth(isFullWidth);
}
public void setUnlockHintRunning(boolean running) {
mAmbientState.setUnlockHintRunning(running);
}
public void setQsCustomizerShowing(boolean isShowing) {
mAmbientState.setQsCustomizerShowing(isShowing);
requestChildrenUpdate();
}
public void setHeadsUpGoingAwayAnimationsAllowed(boolean headsUpGoingAwayAnimationsAllowed) {
mHeadsUpGoingAwayAnimationsAllowed = headsUpGoingAwayAnimationsAllowed;
}
public void setAntiBurnInOffsetX(int antiBurnInOffsetX) {
mAntiBurnInOffsetX = antiBurnInOffsetX;
updatePanelTranslation();
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
+ " alpha:%f scrollY:%d maxTopPadding:%d showShelfOnly=%s"
+ " qsExpandFraction=%f]",
this.getClass().getSimpleName(),
mPulsing ? "T":"f",
mAmbientState.isQsCustomizerShowing() ? "T":"f",
getVisibility() == View.VISIBLE ? "visible"
: getVisibility() == View.GONE ? "gone"
: "invisible",
getAlpha(),
mAmbientState.getScrollY(),
mMaxTopPadding,
mShouldShowShelfOnly ? "T":"f",
mQsExpansionFraction));
}
public boolean isFullyDark() {
return mAmbientState.isFullyDark();
}
public void addOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
mExpandedHeightListeners.add(listener);
}
public void removeOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
mExpandedHeightListeners.remove(listener);
}
public void setHeadsUpAppearanceController(
HeadsUpAppearanceController headsUpAppearanceController) {
mHeadsUpAppearanceController = headsUpAppearanceController;
}
public void setIconAreaController(NotificationIconAreaController controller) {
mIconAreaController = controller;
}
public interface OnEmptySpaceClickListener {
void onEmptySpaceClicked(float x, float y);
}
public interface OnOverscrollTopChangedListener {
void onOverscrollTopChanged(float amount, boolean isRubberbanded);
void flingTopOverscroll(float velocity, boolean open);
}
private class NotificationSwipeHelper extends SwipeHelper
implements NotificationSwipeActionHelper {
private static final long COVER_MENU_DELAY = 4000;
private Runnable mFalsingCheck;
private Handler mHandler;
public NotificationSwipeHelper(int swipeDirection, Callback callback, Context context) {
super(swipeDirection, callback, context);
mHandler = new Handler();
mFalsingCheck = new Runnable() {
@Override
public void run() {
resetExposedMenuView(true , true );
}
};
}
@Override
public void onDownUpdate(View currView, MotionEvent ev) {
mTranslatingParentView = currView;
if (mCurrMenuRow != null) {
mCurrMenuRow.onTouchEvent(currView, ev, 0 );
}
mCurrMenuRow = null;
mHandler.removeCallbacks(mFalsingCheck);
resetExposedMenuView(true , false );
if (currView instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) currView;
mCurrMenuRow = row.createMenu();
mCurrMenuRow.setSwipeActionHelper(NotificationSwipeHelper.this);
mCurrMenuRow.setMenuClickListener(NotificationStackScrollLayout.this);
mCurrMenuRow.onTouchEvent(currView, ev, 0 );
}
}
@Override
public void onMoveUpdate(View view, MotionEvent ev, float translation, float delta) {
mHandler.removeCallbacks(mFalsingCheck);
if (mCurrMenuRow != null) {
mCurrMenuRow.onTouchEvent(view, ev, 0 );
}
}
@Override
public boolean handleUpEvent(MotionEvent ev, View animView, float velocity,
float translation) {
if (mCurrMenuRow != null) {
return mCurrMenuRow.onTouchEvent(animView, ev, velocity);
}
return false;
}
@Override
public void dismissChild(final View view, float velocity,
boolean useAccelerateInterpolator) {
super.dismissChild(view, velocity, useAccelerateInterpolator);
if (mIsExpanded) {
handleChildViewDismissed(view);
}
mStatusBar.getGutsManager().closeAndSaveGuts(true ,
false , false , -1 , -1 ,
false );
handleMenuCoveredOrDismissed();
}
@Override
public void snapChild(final View animView, final float targetLeft, float velocity) {
super.snapChild(animView, targetLeft, velocity);
onDragCancelled(animView);
if (targetLeft == 0) {
handleMenuCoveredOrDismissed();
}
}
@Override
public void snooze(StatusBarNotification sbn, SnoozeOption snoozeOption) {
mStatusBar.setNotificationSnoozed(sbn, snoozeOption);
}
public boolean isFalseGesture(MotionEvent ev) {
return super.isFalseGesture(ev);
}
private void handleMenuCoveredOrDismissed() {
if (mMenuExposedView != null && mMenuExposedView == mTranslatingParentView) {
mMenuExposedView = null;
}
}
@Override
public Animator getViewTranslationAnimator(View v, float target,
AnimatorUpdateListener listener) {
if (v instanceof ExpandableNotificationRow) {
return ((ExpandableNotificationRow) v).getTranslateViewAnimator(target, listener);
} else {
return super.getViewTranslationAnimator(v, target, listener);
}
}
@Override
public void setTranslation(View v, float translate) {
((ExpandableView) v).setTranslation(translate);
}
@Override
public float getTranslation(View v) {
return ((ExpandableView) v).getTranslation();
}
@Override
public void dismiss(View animView, float velocity) {
dismissChild(animView, velocity,
!swipedFastEnough(0, 0) );
}
@Override
public void snap(View animView, float targetLeft, float velocity) {
snapChild(animView, targetLeft, velocity);
}
@Override
public boolean swipedFarEnough(float translation, float viewSize) {
return swipedFarEnough();
}
@Override
public boolean swipedFastEnough(float translation, float velocity) {
return swipedFastEnough();
}
@Override
public float getMinDismissVelocity() {
return getEscapeVelocity();
}
public void onMenuShown(View animView) {
onDragCancelled(animView);
if (isAntiFalsingNeeded()) {
mHandler.removeCallbacks(mFalsingCheck);
mHandler.postDelayed(mFalsingCheck, COVER_MENU_DELAY);
}
}
public void closeControlsIfOutsideTouch(MotionEvent ev) {
NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
View view = null;
if (guts != null && !guts.getGutsContent().isLeavebehind()) {
view = guts;
} else if (mCurrMenuRow != null && mCurrMenuRow.isMenuVisible()
&& mTranslatingParentView != null) {
view = mTranslatingParentView;
}
if (view != null && !isTouchInView(ev, view)) {
mStatusBar.getGutsManager().closeAndSaveGuts(false ,
false , true , -1 , -1 ,
false );
resetExposedMenuView(true , true );
}
}
public void resetExposedMenuView(boolean animate, boolean force) {
if (mMenuExposedView == null
|| (!force && mMenuExposedView == mTranslatingParentView)) {
return;
}
final View prevMenuExposedView = mMenuExposedView;
if (animate) {
Animator anim = getViewTranslationAnimator(prevMenuExposedView,
0 , null );
if (anim != null) {
anim.start();
}
} else if (mMenuExposedView instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) mMenuExposedView;
if (!row.isRemoved()) {
row.resetTranslation();
}
}
mMenuExposedView = null;
}
}
private boolean isTouchInView(MotionEvent ev, View view) {
if (view == null) {
return false;
}
final int height = (view instanceof ExpandableView)
? ((ExpandableView) view).getActualHeight()
: view.getHeight();
final int rx = (int) ev.getRawX();
final int ry = (int) ev.getRawY();
view.getLocationOnScreen(mTempInt2);
final int x = mTempInt2[0];
final int y = mTempInt2[1];
Rect rect = new Rect(x, y, x + view.getWidth(), y + height);
boolean ret = rect.contains(rx, ry);
return ret;
}
private void updateContinuousShadowDrawing() {
boolean continuousShadowUpdate = mAnimationRunning
|| !mAmbientState.getDraggedViews().isEmpty();
if (continuousShadowUpdate != mContinuousShadowUpdate) {
if (continuousShadowUpdate) {
getViewTreeObserver().addOnPreDrawListener(mShadowUpdater);
} else {
getViewTreeObserver().removeOnPreDrawListener(mShadowUpdater);
}
mContinuousShadowUpdate = continuousShadowUpdate;
}
}
@Override
public void resetExposedMenuView(boolean animate, boolean force) {
mSwipeHelper.resetExposedMenuView(animate, force);
}
public void closeControlsIfOutsideTouch(MotionEvent ev) {
mSwipeHelper.closeControlsIfOutsideTouch(ev);
}
static class AnimationEvent {
static AnimationFilter[] FILTERS = new AnimationFilter[] {
new AnimationFilter()
.animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
.animateZ()
.hasDelays(),
new AnimationFilter()
.animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
.animateZ()
.hasDelays(),
new AnimationFilter()
.animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
.animateZ()
.hasDelays(),
new AnimationFilter()
.animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
.animateDimmed()
.animateZ(),
new AnimationFilter()
.animateShadowAlpha(),
new AnimationFilter()
.animateShadowAlpha()
.animateHeight(),
new AnimationFilter()
.animateZ(),
new AnimationFilter()
.animateDimmed(),
new AnimationFilter()
.animateAlpha()
.animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
.animateZ(),
null,
new AnimationFilter()
.animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
.animateDimmed()
.animateZ()
.hasDelays(),
new AnimationFilter()
.animateHideSensitive(),
new AnimationFilter()
.animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
.animateZ(),
new AnimationFilter()
.animateAlpha()
.animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
.animateZ(),
new AnimationFilter()
.animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
.animateZ(),
new AnimationFilter()
.animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
.animateZ()
.hasDelays(),
new AnimationFilter()
.animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
.animateZ()
.hasDelays(),
new AnimationFilter()
.animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
.animateZ(),
new AnimationFilter()
.animateAlpha()
.animateShadowAlpha()
.animateDark()
.animateDimmed()
.animateHideSensitive()
.animateHeight()
.animateTopInset()
.animateY()
.animateZ(),
new AnimationFilter()
.animateAlpha()
.hasDelays()
.animateY(),
new AnimationFilter()
.animateAlpha()
.hasDelays()
.animateY(),
};
static int[] LENGTHS = new int[] {
StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
StackStateAnimator.ANIMATION_DURATION_STANDARD,
StackStateAnimator.ANIMATION_DURATION_STANDARD,
StackStateAnimator.ANIMATION_DURATION_STANDARD,
StackStateAnimator.ANIMATION_DURATION_STANDARD,
StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
StackStateAnimator.ANIMATION_DURATION_STANDARD,
StackStateAnimator.ANIMATION_DURATION_WAKEUP,
StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE,
StackStateAnimator.ANIMATION_DURATION_STANDARD,
StackStateAnimator.ANIMATION_DURATION_STANDARD,
StackStateAnimator.ANIMATION_DURATION_STANDARD,
StackStateAnimator.ANIMATION_DURATION_HEADS_UP_APPEAR,
StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
StackStateAnimator.ANIMATION_DURATION_STANDARD,
StackStateAnimator.ANIMATION_DURATION_STANDARD,
StackStateAnimator.ANIMATION_DURATION_PULSE_APPEAR,
StackStateAnimator.ANIMATION_DURATION_PULSE_APPEAR / 2,
};
static final int ANIMATION_TYPE_ADD = 0;
static final int ANIMATION_TYPE_REMOVE = 1;
static final int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 2;
static final int ANIMATION_TYPE_TOP_PADDING_CHANGED = 3;
static final int ANIMATION_TYPE_START_DRAG = 4;
static final int ANIMATION_TYPE_SNAP_BACK = 5;
static final int ANIMATION_TYPE_ACTIVATED_CHILD = 6;
static final int ANIMATION_TYPE_DIMMED = 7;
static final int ANIMATION_TYPE_CHANGE_POSITION = 8;
static final int ANIMATION_TYPE_DARK = 9;
static final int ANIMATION_TYPE_GO_TO_FULL_SHADE = 10;
static final int ANIMATION_TYPE_HIDE_SENSITIVE = 11;
static final int ANIMATION_TYPE_VIEW_RESIZE = 12;
static final int ANIMATION_TYPE_GROUP_EXPANSION_CHANGED = 13;
static final int ANIMATION_TYPE_HEADS_UP_APPEAR = 14;
static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR = 15;
static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK = 16;
static final int ANIMATION_TYPE_HEADS_UP_OTHER = 17;
static final int ANIMATION_TYPE_EVERYTHING = 18;
static final int ANIMATION_TYPE_PULSE_APPEAR = 19;
static final int ANIMATION_TYPE_PULSE_DISAPPEAR = 20;
static final int DARK_ANIMATION_ORIGIN_INDEX_ABOVE = -1;
static final int DARK_ANIMATION_ORIGIN_INDEX_BELOW = -2;
final long eventStartTime;
final View changingView;
final int animationType;
final AnimationFilter filter;
final long length;
View viewAfterChangingView;
int darkAnimationOriginIndex;
boolean headsUpFromBottom;
AnimationEvent(View view, int type) {
this(view, type, LENGTHS[type]);
}
AnimationEvent(View view, int type, AnimationFilter filter) {
this(view, type, LENGTHS[type], filter);
}
AnimationEvent(View view, int type, long length) {
this(view, type, length, FILTERS[type]);
}
AnimationEvent(View view, int type, long length, AnimationFilter filter) {
eventStartTime = AnimationUtils.currentAnimationTimeMillis();
changingView = view;
animationType = type;
this.length = length;
this.filter = filter;
}
static long combineLength(ArrayList<AnimationEvent> events) {
long length = 0;
int size = events.size();
for (int i = 0; i < size; i++) {
AnimationEvent event = events.get(i);
length = Math.max(length, event.length);
if (event.animationType == ANIMATION_TYPE_GO_TO_FULL_SHADE) {
return event.length;
}
}
return length;
}
}
}