Handling a few more border cases with HUNs
Also does sorting correctly now. The status bar now allows touches below when a heads-up is on. Also fixes a few flashes when a heads up was dismissed or appeared. Change-Id: I4d90a07333ad2e5ea2a13704cdc9d9184716681a
This commit is contained in:
@@ -1880,7 +1880,7 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
}
|
||||
boolean applyInPlace = shouldApplyInPlace(entry, n);
|
||||
final boolean shouldInterrupt = shouldInterrupt(notification);
|
||||
final boolean alertAgain = shouldInterrupt && alertAgain(entry, n);
|
||||
final boolean alertAgain = alertAgain(entry, n);
|
||||
boolean isHeadsUp = shouldInterrupt && alertAgain;
|
||||
|
||||
entry.notification = notification;
|
||||
@@ -1947,8 +1947,12 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
if (wasHeadsUp) {
|
||||
mHeadsUpManager.updateNotification(entry, alertAgain);
|
||||
if (!shouldInterrupt) {
|
||||
// We don't want this to be interrupting anymore, lets remove it
|
||||
mHeadsUpManager.removeNotification(key);
|
||||
}
|
||||
} else if (shouldInterrupt && alertAgain) {
|
||||
// This notification was updated to be a heads-up, show it!
|
||||
mHeadsUpManager.showNotification(entry);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -510,9 +510,6 @@ public class NotificationPanelView extends PanelView implements
|
||||
|
||||
@Override
|
||||
protected void flingToHeight(float vel, boolean expand, float target) {
|
||||
if (!expand && mHeadsUpManager.hasPinnedHeadsUp()) {
|
||||
target = mHeadsUpManager.getHighestPinnedHeadsUp();
|
||||
}
|
||||
mHeadsUpTouchHelper.notifyFling(!expand);
|
||||
super.flingToHeight(vel, expand, target);
|
||||
}
|
||||
@@ -722,7 +719,7 @@ public class NotificationPanelView extends PanelView implements
|
||||
|| event.getActionMasked() == MotionEvent.ACTION_UP) {
|
||||
mConflictingQsExpansionGesture = false;
|
||||
}
|
||||
if (event.getActionMasked() == MotionEvent.ACTION_DOWN && mExpandedHeight == 0
|
||||
if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isShadeCollapsed()
|
||||
&& mQsExpansionEnabled) {
|
||||
mTwoFingerQsExpandPossible = true;
|
||||
}
|
||||
@@ -1774,7 +1771,7 @@ public class NotificationPanelView extends PanelView implements
|
||||
}
|
||||
|
||||
private void updateMaxHeadsUpTranslation() {
|
||||
mNotificationStackScroller.setMaxHeadsUpTranslation(getHeight() - mBottomBarHeight);
|
||||
mNotificationStackScroller.setHeadsUpBoundaries(getHeight(), mBottomBarHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2151,16 +2148,12 @@ public class NotificationPanelView extends PanelView implements
|
||||
|
||||
@Override
|
||||
public void OnHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
|
||||
// TODO: figure out the conditions when not to generate an animation
|
||||
mNotificationStackScroller.generateHeadsUpAnimation(entry.row, isHeadsUp);
|
||||
if (isShadeCollapsed()) {
|
||||
setExpandedHeight(mHeadsUpManager.getHighestPinnedHeadsUp());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isShadeCollapsed() {
|
||||
// TODO: handle this cleaner
|
||||
return mHeader.getTranslationY() + mHeader.getCollapsedHeight() <= 0;
|
||||
@Override
|
||||
protected boolean isShadeCollapsed() {
|
||||
return mExpandedHeight == 0 || mHeadsUpManager.hasPinnedHeadsUp();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -659,7 +659,7 @@ public abstract class PanelView extends FrameLayout {
|
||||
// If the user isn't actively poking us, let's update the height
|
||||
if ((!mTracking || isTrackingBlocked())
|
||||
&& mHeightAnimator == null
|
||||
&& mExpandedHeight > 0
|
||||
&& !isShadeCollapsed()
|
||||
&& currentMaxPanelHeight != mExpandedHeight
|
||||
&& !mPeekPending
|
||||
&& mPeekAnimator == null
|
||||
@@ -730,6 +730,7 @@ public abstract class PanelView extends FrameLayout {
|
||||
}
|
||||
|
||||
public boolean isFullyCollapsed() {
|
||||
// TODO: look into whether this is still correct with HUN's
|
||||
return mExpandedHeight <= 0;
|
||||
}
|
||||
|
||||
@@ -1019,6 +1020,8 @@ public abstract class PanelView extends FrameLayout {
|
||||
*/
|
||||
protected abstract int getClearAllHeight();
|
||||
|
||||
protected abstract boolean isShadeCollapsed();
|
||||
|
||||
public void setHeadsUpManager(HeadsUpManager headsUpManager) {
|
||||
mHeadsUpManager = headsUpManager;
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
|
||||
import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
|
||||
@@ -615,7 +615,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
|
||||
R.color.notification_panel_solid_background)));
|
||||
}
|
||||
|
||||
mHeadsUpManager = new HeadsUpManager(context);
|
||||
mHeadsUpManager = new HeadsUpManager(context, mNotificationPanel.getViewTreeObserver());
|
||||
mHeadsUpManager.setBar(this);
|
||||
mHeadsUpManager.addListener(this);
|
||||
mHeadsUpManager.addListener(mNotificationPanel);
|
||||
@@ -1880,10 +1880,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
|
||||
|
||||
@Override
|
||||
public void escalateHeadsUp() {
|
||||
TreeMap<String, HeadsUpManager.HeadsUpEntry> entries = mHeadsUpManager.getEntries();
|
||||
for (String key : entries.keySet()) {
|
||||
Entry entry = entries.get(key).entry;
|
||||
final StatusBarNotification sbn = entry.notification;
|
||||
TreeSet<HeadsUpManager.HeadsUpEntry> entries = mHeadsUpManager.getSortedEntries();
|
||||
for (HeadsUpManager.HeadsUpEntry entry : entries) {
|
||||
final StatusBarNotification sbn = entry.entry.notification;
|
||||
final Notification notification = sbn.getNotification();
|
||||
if (notification.fullScreenIntent != null) {
|
||||
if (DEBUG) {
|
||||
|
||||
@@ -172,11 +172,20 @@ public class StatusBarWindowManager {
|
||||
applyUserActivityTimeout(state);
|
||||
applyInputFeatures(state);
|
||||
applyFitsSystemWindows(state);
|
||||
applyModalFlag(state);
|
||||
if (mLp.copyFrom(mLpChanged) != 0) {
|
||||
mWindowManager.updateViewLayout(mStatusBarView, mLp);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyModalFlag(State state) {
|
||||
if (state.headsUpShowing) {
|
||||
mLpChanged.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
|
||||
} else {
|
||||
mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
|
||||
}
|
||||
}
|
||||
|
||||
public void setKeyguardShowing(boolean showing) {
|
||||
mCurrentState.keyguardShowing = showing;
|
||||
apply(mCurrentState);
|
||||
|
||||
@@ -30,16 +30,18 @@ import android.view.ViewTreeObserver;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.statusbar.ExpandableNotificationRow;
|
||||
import com.android.systemui.statusbar.NotificationData;
|
||||
import com.android.systemui.statusbar.phone.PhoneStatusBar;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Stack;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
public class HeadsUpManager {
|
||||
public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsListener {
|
||||
private static final String TAG = "HeadsUpManager";
|
||||
private static final boolean DEBUG = false;
|
||||
private static final String SETTING_HEADS_UP_SNOOZE_LENGTH_MS = "heads_up_snooze_length_ms";
|
||||
@@ -76,8 +78,8 @@ public class HeadsUpManager {
|
||||
private PhoneStatusBar mBar;
|
||||
private int mSnoozeLengthMs;
|
||||
private ContentObserver mSettingsObserver;
|
||||
|
||||
private TreeMap<String ,HeadsUpEntry> mHeadsUpEntries = new TreeMap<>();
|
||||
private HashMap<String, HeadsUpEntry> mHeadsUpEntries = new HashMap<>();
|
||||
private TreeSet<HeadsUpEntry> mSortedEntries = new TreeSet<>();
|
||||
private HashSet<String> mSwipedOutKeys = new HashSet<>();
|
||||
private int mUser;
|
||||
private Clock mClock;
|
||||
@@ -86,8 +88,9 @@ public class HeadsUpManager {
|
||||
private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>();
|
||||
private boolean mIsExpanded;
|
||||
private boolean mHasPinnedHeadsUp;
|
||||
private int[] mTmpTwoArray = new int[2];
|
||||
|
||||
public HeadsUpManager(final Context context) {
|
||||
public HeadsUpManager(final Context context, ViewTreeObserver observer) {
|
||||
Resources resources = context.getResources();
|
||||
mTouchSensitivityDelay = resources.getInteger(R.integer.heads_up_sensitivity_delay);
|
||||
if (DEBUG) Log.v(TAG, "create() " + mTouchSensitivityDelay);
|
||||
@@ -95,7 +98,7 @@ public class HeadsUpManager {
|
||||
mDefaultSnoozeLengthMs = resources.getInteger(R.integer.heads_up_default_snooze_length_ms);
|
||||
mSnoozeLengthMs = mDefaultSnoozeLengthMs;
|
||||
mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time);
|
||||
mHeadsUpNotificationDecay = 2000000;
|
||||
mHeadsUpNotificationDecay = 200000000/*resources.getInteger(R.integer.heads_up_notification_decay)*/;;
|
||||
mClock = new Clock();
|
||||
// TODO: shadow mSwipeHelper.setMaxSwipeProgress(mMaxAlpha);
|
||||
|
||||
@@ -116,12 +119,7 @@ public class HeadsUpManager {
|
||||
Settings.Global.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS), false,
|
||||
mSettingsObserver);
|
||||
if (DEBUG) Log.v(TAG, "mSnoozeLengthMs = " + mSnoozeLengthMs);
|
||||
|
||||
// TODO: investigate whether this is still needed
|
||||
// if (!mHeadsUpEntries.isEmpty()) {
|
||||
// whoops, we're on already!
|
||||
// showNotification(mHeadsUpEntries);
|
||||
// }
|
||||
observer.addOnComputeInternalInsetsListener(this);
|
||||
}
|
||||
|
||||
public void setBar(PhoneStatusBar bar) {
|
||||
@@ -144,7 +142,6 @@ public class HeadsUpManager {
|
||||
addHeadsUpEntry(headsUp);
|
||||
updateNotification(headsUp, true);
|
||||
headsUp.setInterruption();
|
||||
updatePinnedHeadsUpState(false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,26 +161,32 @@ public class HeadsUpManager {
|
||||
}
|
||||
|
||||
private void addHeadsUpEntry(NotificationData.Entry entry) {
|
||||
boolean wasEmpty = mHeadsUpEntries.isEmpty();
|
||||
HeadsUpEntry headsUpEntry = mEntryPool.acquire();
|
||||
|
||||
// This will also add the entry to the sortedList
|
||||
headsUpEntry.setEntry(entry);
|
||||
mHeadsUpEntries.put(entry.key, headsUpEntry);
|
||||
entry.row.setHeadsUp(true);
|
||||
if (!entry.row.isInShade() && mIsExpanded) {
|
||||
headsUpEntry.entry.row.setInShade(true);
|
||||
}
|
||||
updatePinnedHeadsUpState(false);
|
||||
for (OnHeadsUpChangedListener listener : mListeners) {
|
||||
listener.OnHeadsUpStateChanged(entry, true);
|
||||
}
|
||||
entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
|
||||
entry.row.setHeadsUp(true);
|
||||
}
|
||||
|
||||
private void removeHeadsUpEntry(NotificationData.Entry entry) {
|
||||
HeadsUpEntry remove = mHeadsUpEntries.remove(entry.key);
|
||||
mSortedEntries.remove(remove);
|
||||
mEntryPool.release(remove);
|
||||
entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
|
||||
entry.row.setHeadsUp(false);
|
||||
updatePinnedHeadsUpState(false);
|
||||
for (OnHeadsUpChangedListener listener : mListeners) {
|
||||
listener.OnHeadsUpStateChanged(entry, false);
|
||||
}
|
||||
updatePinnedHeadsUpState(false);
|
||||
}
|
||||
|
||||
private void updatePinnedHeadsUpState(boolean forceImmediate) {
|
||||
@@ -238,7 +241,8 @@ public class HeadsUpManager {
|
||||
*/
|
||||
public void releaseAllImmediately() {
|
||||
if (DEBUG) Log.v(TAG, "releaseAllImmediately");
|
||||
for (String key: mHeadsUpEntries.keySet()) {
|
||||
HashSet<String> keys = new HashSet<>(mHeadsUpEntries.keySet());
|
||||
for (String key: keys) {
|
||||
releaseImmediately(key);
|
||||
}
|
||||
}
|
||||
@@ -287,12 +291,12 @@ public class HeadsUpManager {
|
||||
return mHeadsUpEntries.get(key).entry;
|
||||
}
|
||||
|
||||
public TreeMap<String, HeadsUpEntry> getEntries() {
|
||||
return mHeadsUpEntries;
|
||||
public TreeSet<HeadsUpEntry> getSortedEntries() {
|
||||
return mSortedEntries;
|
||||
}
|
||||
|
||||
public HeadsUpEntry getTopEntry() {
|
||||
return mHeadsUpEntries.isEmpty() ? null : mHeadsUpEntries.lastEntry().getValue();
|
||||
return mSortedEntries.isEmpty() ? null : mSortedEntries.first();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -313,13 +317,25 @@ public class HeadsUpManager {
|
||||
}
|
||||
|
||||
public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
|
||||
// TODO: Look into touchable region
|
||||
// mContentHolder.getLocationOnScreen(mTmpTwoArray);
|
||||
//
|
||||
// info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
|
||||
// info.touchableRegion.set(mTmpTwoArray[0], mTmpTwoArray[1],
|
||||
// mTmpTwoArray[0] + mContentHolder.getWidth(),
|
||||
// mTmpTwoArray[1] + mContentHolder.getHeight());
|
||||
if (!mIsExpanded && mHasPinnedHeadsUp) {
|
||||
int minX = Integer.MAX_VALUE;
|
||||
int maxX = 0;
|
||||
int minY = Integer.MAX_VALUE;
|
||||
int maxY = 0;
|
||||
for (HeadsUpEntry entry: mSortedEntries) {
|
||||
ExpandableNotificationRow row = entry.entry.row;
|
||||
if (!row.isInShade()) {
|
||||
row.getLocationOnScreen(mTmpTwoArray);
|
||||
minX = Math.min(minX, mTmpTwoArray[0]);
|
||||
minY = Math.min(minY, 0);
|
||||
maxX = Math.max(maxX, mTmpTwoArray[0] + row.getWidth());
|
||||
maxY = Math.max(maxY, row.getHeadsUpHeight());
|
||||
}
|
||||
}
|
||||
|
||||
info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
|
||||
info.touchableRegion.set(minX, minY, maxX, maxY);
|
||||
}
|
||||
}
|
||||
|
||||
public void setUser(int user) {
|
||||
@@ -332,8 +348,8 @@ public class HeadsUpManager {
|
||||
pw.print(" mSnoozeLengthMs="); pw.println(mSnoozeLengthMs);
|
||||
pw.print(" now="); pw.println(SystemClock.elapsedRealtime());
|
||||
pw.print(" mUser="); pw.println(mUser);
|
||||
for (String key: mHeadsUpEntries.keySet()) {
|
||||
pw.print(" HeadsUpEntry="); pw.println(mHeadsUpEntries.get(key));
|
||||
for (HeadsUpEntry entry: mSortedEntries) {
|
||||
pw.print(" HeadsUpEntry="); pw.println(entry.entry);
|
||||
}
|
||||
int N = mSnoozedPackages.size();
|
||||
pw.println(" snoozed packages: " + N);
|
||||
@@ -363,8 +379,7 @@ public class HeadsUpManager {
|
||||
|
||||
public float getHighestPinnedHeadsUp() {
|
||||
float max = 0;
|
||||
for (String key: mHeadsUpEntries.keySet()) {
|
||||
HeadsUpEntry entry = mHeadsUpEntries.get(key);
|
||||
for (HeadsUpEntry entry: mSortedEntries) {
|
||||
if (!entry.entry.row.isInShade()) {
|
||||
max = Math.max(max, entry.entry.row.getActualHeight());
|
||||
}
|
||||
@@ -437,13 +452,14 @@ public class HeadsUpManager {
|
||||
earliestRemovaltime = currentTime + mMinimumDisplayTime;
|
||||
removeAutoCancelCallbacks();
|
||||
mHandler.postDelayed(mRemoveHeadsUpRunnable, removeDelay);
|
||||
updateSortOrder(HeadsUpEntry.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(HeadsUpEntry o) {
|
||||
return postTime < o.postTime ? -1
|
||||
return postTime < o.postTime ? 1
|
||||
: postTime == o.postTime ? 0
|
||||
: 1;
|
||||
: -1;
|
||||
}
|
||||
|
||||
public void removeAutoCancelCallbacks() {
|
||||
@@ -461,6 +477,16 @@ public class HeadsUpManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the sorted heads up order.
|
||||
*
|
||||
* @param headsUpEntry the headsUp that changed
|
||||
*/
|
||||
private void updateSortOrder(HeadsUpEntry headsUpEntry) {
|
||||
mSortedEntries.remove(headsUpEntry);
|
||||
mSortedEntries.add(headsUpEntry);
|
||||
}
|
||||
|
||||
public static class Clock {
|
||||
public long currentTimeMillis() {
|
||||
return SystemClock.elapsedRealtime();
|
||||
|
||||
@@ -17,11 +17,13 @@
|
||||
package com.android.systemui.statusbar.stack;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import com.android.systemui.statusbar.ActivatableNotificationView;
|
||||
import com.android.systemui.statusbar.ExpandableNotificationRow;
|
||||
import com.android.systemui.statusbar.policy.HeadsUpManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* A global state to track all input states for the algorithm.
|
||||
@@ -37,7 +39,7 @@ public class AmbientState {
|
||||
private boolean mDark;
|
||||
private boolean mHideSensitive;
|
||||
private HeadsUpManager mHeadsUpManager;
|
||||
private float mPaddingOffset;
|
||||
private float mStackTranslation;
|
||||
private int mLayoutHeight;
|
||||
private int mTopPadding;
|
||||
private boolean mShadeExpanded;
|
||||
@@ -128,16 +130,16 @@ public class AmbientState {
|
||||
mHeadsUpManager = headsUpManager;
|
||||
}
|
||||
|
||||
public TreeMap<String, HeadsUpManager.HeadsUpEntry> getHeadsUpEntries() {
|
||||
return mHeadsUpManager.getEntries();
|
||||
public TreeSet<HeadsUpManager.HeadsUpEntry> getSortedHeadsUpEntries() {
|
||||
return mHeadsUpManager.getSortedEntries();
|
||||
}
|
||||
|
||||
public float getPaddingOffset() {
|
||||
return mPaddingOffset;
|
||||
public float getStackTranslation() {
|
||||
return mStackTranslation;
|
||||
}
|
||||
|
||||
public void setPaddingOffset(float paddingOffset) {
|
||||
mPaddingOffset = paddingOffset;
|
||||
public void setStackTranslation(float stackTranslation) {
|
||||
mStackTranslation = stackTranslation;
|
||||
}
|
||||
|
||||
public int getLayoutHeight() {
|
||||
@@ -148,7 +150,7 @@ public class AmbientState {
|
||||
mLayoutHeight = layoutHeight;
|
||||
}
|
||||
|
||||
public int getTopPadding() {
|
||||
public float getTopPadding() {
|
||||
return mTopPadding;
|
||||
}
|
||||
|
||||
@@ -157,7 +159,13 @@ public class AmbientState {
|
||||
}
|
||||
|
||||
public int getInnerHeight() {
|
||||
return mLayoutHeight - mTopPadding;
|
||||
return mLayoutHeight - mTopPadding - getTopHeadsUpPushIn();
|
||||
}
|
||||
|
||||
private int getTopHeadsUpPushIn() {
|
||||
ExpandableNotificationRow topHeadsUpEntry = getTopHeadsUpEntry();
|
||||
return topHeadsUpEntry != null ? topHeadsUpEntry.getHeadsUpHeight()
|
||||
- topHeadsUpEntry.getMinHeight(): 0;
|
||||
}
|
||||
|
||||
public boolean isShadeExpanded() {
|
||||
@@ -175,4 +183,9 @@ public class AmbientState {
|
||||
public float getMaxHeadsUpTranslation() {
|
||||
return mMaxHeadsUpTranslation;
|
||||
}
|
||||
|
||||
public ExpandableNotificationRow getTopHeadsUpEntry() {
|
||||
HeadsUpManager.HeadsUpEntry topEntry = mHeadsUpManager.getTopEntry();
|
||||
return topEntry == null ? null : topEntry.entry.row;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,15 +123,15 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
private StackScrollState mCurrentStackScrollState = new StackScrollState(this);
|
||||
private AmbientState mAmbientState = new AmbientState();
|
||||
private NotificationGroupManager mGroupManager;
|
||||
private ArrayList<View> mChildrenToAddAnimated = new ArrayList<View>();
|
||||
private ArrayList<View> mChildrenToRemoveAnimated = new ArrayList<View>();
|
||||
private ArrayList<View> mSnappedBackChildren = new ArrayList<View>();
|
||||
private ArrayList<View> mDragAnimPendingChildren = new ArrayList<View>();
|
||||
private ArrayList<View> mChildrenChangingPositions = new ArrayList<View>();
|
||||
private ArrayList<View> mChildrenToAddAnimated = new ArrayList<>();
|
||||
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<AnimationEvent>();
|
||||
private ArrayList<View> mSwipedOutViews = new ArrayList<View>();
|
||||
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;
|
||||
@@ -145,7 +145,6 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
* The raw amount of the overScroll on the bottom, which is not rubber-banded.
|
||||
*/
|
||||
private float mOverScrolledBottomPixels;
|
||||
|
||||
private OnChildLocationsChangedListener mListener;
|
||||
private OnOverscrollTopChangedListener mOverscrollTopChangedListener;
|
||||
private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
|
||||
@@ -173,7 +172,6 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
* Was the scroller scrolled to the top when the down motion was observed?
|
||||
*/
|
||||
private boolean mScrolledToTopOnFirstDown;
|
||||
|
||||
/**
|
||||
* The minimal amount of over scroll which is needed in order to switch to the quick settings
|
||||
* when over scrolling on a expanded card.
|
||||
@@ -187,15 +185,14 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
private boolean mRequestViewResizeAnimationOnLayout;
|
||||
private boolean mNeedViewResizeAnimation;
|
||||
private View mExpandedGroupView;
|
||||
|
||||
private boolean mEverythingNeedsAnimation;
|
||||
|
||||
/**
|
||||
* The maximum scrollPosition which we are allowed to reach when a notification was expanded.
|
||||
* This is needed to avoid scrolling too far after the notification was collapsed in the same
|
||||
* motion.
|
||||
*/
|
||||
private int mMaxScrollAfterExpand;
|
||||
|
||||
private SwipeHelper.LongPressListener mLongPressListener;
|
||||
|
||||
/**
|
||||
@@ -206,8 +203,8 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
private ViewGroup mScrollView;
|
||||
private boolean mInterceptDelegateEnabled;
|
||||
private boolean mDelegateToScrollView;
|
||||
private boolean mDisallowScrollingInThisMotion;
|
||||
|
||||
private boolean mDisallowScrollingInThisMotion;
|
||||
private long mGoToFullShadeDelay;
|
||||
private ViewTreeObserver.OnPreDrawListener mChildrenUpdater
|
||||
= new ViewTreeObserver.OnPreDrawListener() {
|
||||
@@ -529,7 +526,7 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
private void setPaddingOffset(float paddingOffset) {
|
||||
if (paddingOffset != mPaddingOffset) {
|
||||
mPaddingOffset = paddingOffset;
|
||||
mAmbientState.setPaddingOffset(paddingOffset);
|
||||
mAmbientState.setStackTranslation(paddingOffset);
|
||||
requestChildrenUpdate();
|
||||
}
|
||||
}
|
||||
@@ -617,13 +614,29 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
public void onBeginDrag(View v) {
|
||||
setSwipingInProgress(true);
|
||||
mAmbientState.onBeginDrag(v);
|
||||
if (mAnimationsEnabled) {
|
||||
if (mAnimationsEnabled && !isPinnedHeadsUp(v)) {
|
||||
mDragAnimPendingChildren.add(v);
|
||||
mNeedsAnimation = true;
|
||||
}
|
||||
requestChildrenUpdate();
|
||||
}
|
||||
|
||||
private boolean isPinnedHeadsUp(View v) {
|
||||
if (v instanceof ExpandableNotificationRow) {
|
||||
ExpandableNotificationRow row = (ExpandableNotificationRow) v;
|
||||
return row.isHeadsUp() && !row.isInShade();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isHeadsUp(View v) {
|
||||
if (v instanceof ExpandableNotificationRow) {
|
||||
ExpandableNotificationRow row = (ExpandableNotificationRow) v;
|
||||
return row.isHeadsUp();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void onDragCancelled(View v) {
|
||||
setSwipingInProgress(false);
|
||||
}
|
||||
@@ -693,6 +706,10 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
if (touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
|
||||
if (slidingChild instanceof ExpandableNotificationRow) {
|
||||
ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
|
||||
if (row.isHeadsUp() && !row.isInShade()
|
||||
&& mHeadsUpManager.getTopEntry().entry.row != row) {
|
||||
continue;
|
||||
}
|
||||
return row.getViewAtPosition(touchY - childTop);
|
||||
}
|
||||
return slidingChild;
|
||||
@@ -1788,6 +1805,10 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
}
|
||||
mNeedsAnimation = true;
|
||||
}
|
||||
if (isHeadsUp(child)) {
|
||||
mAddedHeadsUpChildren.add(child);
|
||||
mChildrenToAddAnimated.remove(child);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1826,6 +1847,7 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
}
|
||||
|
||||
private void generateChildHierarchyEvents() {
|
||||
generateHeadsUpAnimationEvents();
|
||||
generateChildRemovalEvents();
|
||||
generateChildAdditionEvents();
|
||||
generatePositionChangeEvents();
|
||||
@@ -1839,19 +1861,42 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
generateGoToFullShadeEvent();
|
||||
generateViewResizeEvent();
|
||||
generateGroupExpansionEvent();
|
||||
generateHeadsUpAnimationEvents();
|
||||
generateAnimateEverythingEvent();
|
||||
mNeedsAnimation = false;
|
||||
}
|
||||
|
||||
private void generateHeadsUpAnimationEvents() {
|
||||
for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
|
||||
int type = eventPair.second ? AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR
|
||||
: AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR;
|
||||
mAnimationEvents.add(new AnimationEvent(eventPair.first,
|
||||
type));
|
||||
ExpandableNotificationRow row = eventPair.first;
|
||||
boolean isHeadsUp = eventPair.second;
|
||||
int type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_OTHER;
|
||||
boolean onBottom = false;
|
||||
if (!row.isInShade() && !isHeadsUp) {
|
||||
type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR;
|
||||
} else if (mAddedHeadsUpChildren.contains(row)) {
|
||||
if (!row.isInShade() || shouldHunAppearFromBottom(row)) {
|
||||
// Our custom add animation
|
||||
type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR;
|
||||
} else {
|
||||
// Normal add animation
|
||||
type = AnimationEvent.ANIMATION_TYPE_ADD;
|
||||
}
|
||||
onBottom = row.isInShade();
|
||||
}
|
||||
AnimationEvent event = new AnimationEvent(row, type);
|
||||
event.headsUpFromBottom = onBottom;
|
||||
mAnimationEvents.add(event);
|
||||
}
|
||||
mHeadsUpChangeAnimations.clear();
|
||||
mAddedHeadsUpChildren.clear();
|
||||
}
|
||||
|
||||
private boolean shouldHunAppearFromBottom(ExpandableNotificationRow row) {
|
||||
StackViewState viewState = mCurrentStackScrollState.getViewStateForView(row);
|
||||
if (viewState.yTranslation + viewState.height < mAmbientState.getMaxHeadsUpTranslation()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void generateGroupExpansionEvent() {
|
||||
@@ -2649,10 +2694,19 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
|
||||
public void setShadeExpanded(boolean shadeExpanded) {
|
||||
mAmbientState.setShadeExpanded(shadeExpanded);
|
||||
mStateAnimator.setShadeExpanded(shadeExpanded);
|
||||
}
|
||||
|
||||
public void setMaxHeadsUpTranslation(int maxTranslation) {
|
||||
mAmbientState.setMaxHeadsUpTranslation(maxTranslation);
|
||||
/**
|
||||
* Set the boundary for the bottom heads up position. The heads up will always be above this
|
||||
* position.
|
||||
*
|
||||
* @param height the height of the screen
|
||||
* @param bottomBarHeight the height of the bar on the bottom
|
||||
*/
|
||||
public void setHeadsUpBoundaries(int height, int bottomBarHeight) {
|
||||
mAmbientState.setMaxHeadsUpTranslation(height - bottomBarHeight);
|
||||
mStateAnimator.setHeadsUpAppearHeightBottom(height);
|
||||
requestChildrenUpdate();
|
||||
}
|
||||
|
||||
@@ -2820,6 +2874,14 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
.animateY()
|
||||
.animateZ(),
|
||||
|
||||
// ANIMATION_TYPE_HEADS_UP_OTHER
|
||||
new AnimationFilter()
|
||||
.animateAlpha()
|
||||
.animateHeight()
|
||||
.animateTopInset()
|
||||
.animateY()
|
||||
.animateZ(),
|
||||
|
||||
// ANIMATION_TYPE_EVERYTHING
|
||||
new AnimationFilter()
|
||||
.animateAlpha()
|
||||
@@ -2883,6 +2945,9 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
// ANIMATION_TYPE_HEADS_UP_DISAPPEAR
|
||||
StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
|
||||
|
||||
// ANIMATION_TYPE_HEADS_UP_OTHER
|
||||
StackStateAnimator.ANIMATION_DURATION_STANDARD,
|
||||
|
||||
// ANIMATION_TYPE_EVERYTHING
|
||||
StackStateAnimator.ANIMATION_DURATION_STANDARD,
|
||||
};
|
||||
@@ -2903,7 +2968,8 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
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_EVERYTHING = 16;
|
||||
static final int ANIMATION_TYPE_HEADS_UP_OTHER = 16;
|
||||
static final int ANIMATION_TYPE_EVERYTHING = 17;
|
||||
|
||||
static final int DARK_ANIMATION_ORIGIN_INDEX_ABOVE = -1;
|
||||
static final int DARK_ANIMATION_ORIGIN_INDEX_BELOW = -2;
|
||||
@@ -2915,6 +2981,7 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
final long length;
|
||||
View viewAfterChangingView;
|
||||
int darkAnimationOriginIndex;
|
||||
boolean headsUpFromBottom;
|
||||
|
||||
AnimationEvent(View view, int type) {
|
||||
this(view, type, LENGTHS[type]);
|
||||
|
||||
@@ -29,7 +29,7 @@ import com.android.systemui.statusbar.policy.HeadsUpManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* The Algorithm of the {@link com.android.systemui.statusbar.stack
|
||||
@@ -167,7 +167,7 @@ public class StackScrollAlgorithm {
|
||||
|
||||
handleDraggedViews(ambientState, resultState, algorithmState);
|
||||
updateDimmedActivatedHideSensitive(ambientState, resultState, algorithmState);
|
||||
updateClipping(resultState, algorithmState);
|
||||
updateClipping(resultState, algorithmState, ambientState);
|
||||
updateSpeedBumpState(resultState, algorithmState, ambientState.getSpeedBumpIndex());
|
||||
getNotificationChildrenStates(resultState, algorithmState);
|
||||
}
|
||||
@@ -198,7 +198,7 @@ public class StackScrollAlgorithm {
|
||||
}
|
||||
|
||||
private void updateClipping(StackScrollState resultState,
|
||||
StackScrollAlgorithmState algorithmState) {
|
||||
StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
|
||||
float previousNotificationEnd = 0;
|
||||
float previousNotificationStart = 0;
|
||||
boolean previousNotificationIsSwiped = false;
|
||||
@@ -239,7 +239,7 @@ public class StackScrollAlgorithm {
|
||||
// otherwise we would clip to a transparent view.
|
||||
previousNotificationStart = newYTranslation + state.clipTopAmount * state.scale;
|
||||
previousNotificationEnd = newNotificationEnd;
|
||||
previousNotificationIsSwiped = child.getTranslationX() != 0;
|
||||
previousNotificationIsSwiped = ambientState.getDraggedViews().contains(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -311,7 +311,9 @@ public class StackScrollAlgorithm {
|
||||
StackViewState viewState = resultState.getViewStateForView(
|
||||
nextChild);
|
||||
// The child below the dragged one must be fully visible
|
||||
viewState.alpha = 1;
|
||||
if (!isPinnedHeadsUpView(draggedView) || isPinnedHeadsUpView(nextChild)) {
|
||||
viewState.alpha = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Lets set the alpha to the one it currently has, as its currently being dragged
|
||||
@@ -322,6 +324,14 @@ public class StackScrollAlgorithm {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isPinnedHeadsUpView(View view) {
|
||||
if (view instanceof ExpandableNotificationRow) {
|
||||
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
|
||||
return row.isHeadsUp() && !row.isInShade();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the visible children on the state.
|
||||
*/
|
||||
@@ -332,16 +342,15 @@ public class StackScrollAlgorithm {
|
||||
state.visibleChildren.clear();
|
||||
state.visibleChildren.ensureCapacity(childCount);
|
||||
int notGoneIndex = 0;
|
||||
TreeMap<String, HeadsUpManager.HeadsUpEntry> headsUpEntries =
|
||||
ambientState.getHeadsUpEntries();
|
||||
for (String key: headsUpEntries.keySet()) {
|
||||
ExpandableView v = headsUpEntries.get(key).entry.row;
|
||||
TreeSet<HeadsUpManager.HeadsUpEntry> headsUpEntries
|
||||
= ambientState.getSortedHeadsUpEntries();
|
||||
for (HeadsUpManager.HeadsUpEntry entry: headsUpEntries) {
|
||||
ExpandableView v = entry.entry.row;
|
||||
notGoneIndex = updateNotGoneIndex(resultState, state, notGoneIndex, v);
|
||||
}
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
ExpandableView v = (ExpandableView) hostView.getChildAt(i);
|
||||
if (v.getVisibility() != View.GONE) {
|
||||
|
||||
if (v instanceof ExpandableNotificationRow) {
|
||||
ExpandableNotificationRow row = (ExpandableNotificationRow) v;
|
||||
if (row.isHeadsUp()) {
|
||||
@@ -401,13 +410,17 @@ public class StackScrollAlgorithm {
|
||||
// How far in is the element currently transitioning into the bottom stack.
|
||||
float yPositionInScrollView = 0.0f;
|
||||
|
||||
// If we have a heads-up higher than the collapsed height we need to add the difference to
|
||||
// the padding of all other elements, i.e push in the top stack slightly.
|
||||
ExpandableNotificationRow topHeadsUpEntry = ambientState.getTopHeadsUpEntry();
|
||||
|
||||
int childCount = algorithmState.visibleChildren.size();
|
||||
int numberOfElementsCompletelyIn = (int) algorithmState.itemsInTopStack;
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
ExpandableView child = algorithmState.visibleChildren.get(i);
|
||||
StackViewState childViewState = resultState.getViewStateForView(child);
|
||||
childViewState.location = StackViewState.LOCATION_UNKNOWN;
|
||||
int childHeight = getMaxAllowedChildHeight(child);
|
||||
int childHeight = getMaxAllowedChildHeight(child, ambientState);
|
||||
float yPositionInScrollViewAfterElement = yPositionInScrollView
|
||||
+ childHeight
|
||||
+ mPaddingBetweenElements;
|
||||
@@ -486,45 +499,31 @@ public class StackScrollAlgorithm {
|
||||
currentYPosition = childViewState.yTranslation + childHeight + mPaddingBetweenElements;
|
||||
yPositionInScrollView = yPositionInScrollViewAfterElement;
|
||||
|
||||
if (ambientState.isShadeExpanded() && topHeadsUpEntry != null
|
||||
&& child != topHeadsUpEntry) {
|
||||
childViewState.yTranslation += topHeadsUpEntry.getHeadsUpHeight() - mCollapsedSize;
|
||||
}
|
||||
childViewState.yTranslation += ambientState.getTopPadding()
|
||||
+ ambientState.getPaddingOffset();
|
||||
|
||||
updateHeadsUpStates(resultState, algorithmState, ambientState);
|
||||
+ ambientState.getStackTranslation();
|
||||
}
|
||||
updateHeadsUpStates(resultState, algorithmState, ambientState);
|
||||
}
|
||||
|
||||
private void updateHeadsUpStates(StackScrollState resultState,
|
||||
StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
|
||||
TreeMap<String, HeadsUpManager.HeadsUpEntry> headsUpEntries =
|
||||
ambientState.getHeadsUpEntries();
|
||||
boolean hasPinnedHeadsUp = false;
|
||||
for (String key: headsUpEntries.keySet()) {
|
||||
ExpandableNotificationRow row = headsUpEntries.get(key).entry.row;
|
||||
TreeSet<HeadsUpManager.HeadsUpEntry> headsUpEntries = ambientState.getSortedHeadsUpEntries();
|
||||
for (HeadsUpManager.HeadsUpEntry entry: headsUpEntries) {
|
||||
ExpandableNotificationRow row = entry.entry.row;
|
||||
StackViewState childState = resultState.getViewStateForView(row);
|
||||
if (!row.isInShade()) {
|
||||
childState.yTranslation = Math.max(childState.yTranslation, 0);
|
||||
hasPinnedHeadsUp = true;
|
||||
childState.yTranslation = 0;
|
||||
}
|
||||
childState.height = Math.max(childState.height, row.getHeadsUpHeight());
|
||||
|
||||
// Ensure that the heads up is always visible even when scrolled of from the bottom
|
||||
childState.yTranslation = Math.min(childState.yTranslation,
|
||||
ambientState.getMaxHeadsUpTranslation() - childState.height);
|
||||
}
|
||||
if (hasPinnedHeadsUp && !ambientState.isShadeExpanded()) {
|
||||
// Let's hide all normal views
|
||||
int childCount = algorithmState.visibleChildren.size();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
ExpandableView child = algorithmState.visibleChildren.get(i);
|
||||
StackViewState state = resultState.getViewStateForView(child);
|
||||
boolean hideView = true;
|
||||
if (child instanceof ExpandableNotificationRow) {
|
||||
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
|
||||
hideView = !row.isHeadsUp();
|
||||
}
|
||||
if (hideView) {
|
||||
state.alpha = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -555,7 +554,7 @@ public class StackScrollAlgorithm {
|
||||
|
||||
/**
|
||||
* Clamp the yTranslation of the child up such that its end is at lest on the end of the top
|
||||
* stack.get
|
||||
* stack.
|
||||
*
|
||||
* @param childViewState the view state of the child
|
||||
* @param childHeight the height of this child
|
||||
@@ -566,9 +565,12 @@ public class StackScrollAlgorithm {
|
||||
mCollapsedSize - childHeight);
|
||||
}
|
||||
|
||||
private int getMaxAllowedChildHeight(View child) {
|
||||
private int getMaxAllowedChildHeight(View child, AmbientState ambientState) {
|
||||
if (child instanceof ExpandableNotificationRow) {
|
||||
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
|
||||
if (ambientState != null && ambientState.getTopHeadsUpEntry() == child) {
|
||||
return mCollapsedSize;
|
||||
}
|
||||
return row.getIntrinsicHeight();
|
||||
} else if (child instanceof ExpandableView) {
|
||||
ExpandableView expandableView = (ExpandableView) child;
|
||||
@@ -695,7 +697,7 @@ public class StackScrollAlgorithm {
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
ExpandableView child = algorithmState.visibleChildren.get(i);
|
||||
StackViewState childViewState = resultState.getViewStateForView(child);
|
||||
int childHeight = getMaxAllowedChildHeight(child);
|
||||
int childHeight = getMaxAllowedChildHeight(child, ambientState);
|
||||
float yPositionInScrollViewAfterElement = yPositionInScrollView
|
||||
+ childHeight
|
||||
+ mPaddingBetweenElements;
|
||||
@@ -850,7 +852,7 @@ public class StackScrollAlgorithm {
|
||||
int oldBottom) {
|
||||
if (mFirstChildWhileExpanding != null) {
|
||||
mFirstChildMaxHeight = getMaxAllowedChildHeight(
|
||||
mFirstChildWhileExpanding);
|
||||
mFirstChildWhileExpanding, null);
|
||||
} else {
|
||||
mFirstChildMaxHeight = 0;
|
||||
}
|
||||
@@ -858,7 +860,7 @@ public class StackScrollAlgorithm {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
mFirstChildMaxHeight = getMaxAllowedChildHeight(mFirstChildWhileExpanding);
|
||||
mFirstChildMaxHeight = getMaxAllowedChildHeight(mFirstChildWhileExpanding, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,9 +21,11 @@ import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.PropertyValuesHolder;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.graphics.Path;
|
||||
import android.view.View;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.view.animation.PathInterpolator;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.statusbar.ExpandableNotificationRow;
|
||||
@@ -32,7 +34,6 @@ import com.android.systemui.statusbar.SpeedBumpView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
|
||||
/**
|
||||
@@ -46,7 +47,7 @@ public class StackStateAnimator {
|
||||
public static final int ANIMATION_DURATION_EXPAND_CLICKED = 360;
|
||||
public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220;
|
||||
public static final int ANIMATION_DURATION_HEADS_UP_APPEAR = 280;
|
||||
public static final int ANIMATION_DURATION_HEADS_UP_DISAPPEAR = 220;
|
||||
public static final int ANIMATION_DURATION_HEADS_UP_DISAPPEAR = 230;
|
||||
public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80;
|
||||
public static final int ANIMATION_DELAY_PER_ELEMENT_EXPAND_CHILDREN = 54;
|
||||
public static final int ANIMATION_DELAY_PER_ELEMENT_MANUAL = 32;
|
||||
@@ -75,12 +76,15 @@ public class StackStateAnimator {
|
||||
private static final int TAG_START_TOP_INSET = R.id.top_inset_animator_start_value_tag;
|
||||
|
||||
private final Interpolator mFastOutSlowInInterpolator;
|
||||
private final Interpolator mHeadsUpAppearInterpolator;
|
||||
private final int mGoToFullShadeAppearingTranslation;
|
||||
public NotificationStackScrollLayout mHostLayout;
|
||||
private ArrayList<NotificationStackScrollLayout.AnimationEvent> mNewEvents =
|
||||
new ArrayList<>();
|
||||
private ArrayList<View> mNewAddChildren = new ArrayList<>();
|
||||
private Set<Animator> mAnimatorSet = new HashSet<>();
|
||||
private HashSet<View> mHeadsUpAppearChildren = new HashSet<>();
|
||||
private HashSet<View> mHeadsUpDisappearChildren = new HashSet<>();
|
||||
private HashSet<Animator> mAnimatorSet = new HashSet<>();
|
||||
private Stack<AnimatorListenerAdapter> mAnimationListenerPool = new Stack<>();
|
||||
private AnimationFilter mAnimationFilter = new AnimationFilter();
|
||||
private long mCurrentLength;
|
||||
@@ -88,11 +92,12 @@ public class StackStateAnimator {
|
||||
|
||||
/** The current index for the last child which was not added in this event set. */
|
||||
private int mCurrentLastNotAddedIndex;
|
||||
|
||||
private ValueAnimator mTopOverScrollAnimator;
|
||||
private ValueAnimator mBottomOverScrollAnimator;
|
||||
private ExpandableNotificationRow mChildExpandingView;
|
||||
private StackViewState mTmpState = new StackViewState();
|
||||
private int mHeadsUpAppearHeightBottom;
|
||||
private boolean mShadeExpanded;
|
||||
|
||||
public StackStateAnimator(NotificationStackScrollLayout hostLayout) {
|
||||
mHostLayout = hostLayout;
|
||||
@@ -101,6 +106,10 @@ public class StackStateAnimator {
|
||||
mGoToFullShadeAppearingTranslation =
|
||||
hostLayout.getContext().getResources().getDimensionPixelSize(
|
||||
R.dimen.go_to_full_shade_appearing_translation);
|
||||
Path path = new Path();
|
||||
path.moveTo(0, 0);
|
||||
path.cubicTo(0.8f, 0, 0.8f, 1.2f, 1f, 1f);
|
||||
mHeadsUpAppearInterpolator = new PathInterpolator(path);
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
@@ -122,7 +131,8 @@ public class StackStateAnimator {
|
||||
final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
|
||||
|
||||
StackViewState viewState = finalState.getViewStateForView(child);
|
||||
if (viewState == null || child.getVisibility() == View.GONE) {
|
||||
if (viewState == null || child.getVisibility() == View.GONE
|
||||
|| applyWithoutAnimation(child, viewState, finalState)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -133,11 +143,35 @@ public class StackStateAnimator {
|
||||
// no child has preformed any animation, lets finish
|
||||
onAnimationFinished();
|
||||
}
|
||||
mHeadsUpAppearChildren.clear();
|
||||
mHeadsUpDisappearChildren.clear();
|
||||
mNewEvents.clear();
|
||||
mNewAddChildren.clear();
|
||||
mChildExpandingView = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a view should not perform an animation and applies it directly.
|
||||
*
|
||||
* @return true if no animation should be performed
|
||||
*/
|
||||
private boolean applyWithoutAnimation(ExpandableView child, StackViewState viewState,
|
||||
StackScrollState finalState) {
|
||||
if (mShadeExpanded) {
|
||||
return false;
|
||||
}
|
||||
if (getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y) != null) {
|
||||
// A Y translation animation is running
|
||||
return false;
|
||||
}
|
||||
if (mHeadsUpDisappearChildren.contains(child) || mHeadsUpAppearChildren.contains(child)) {
|
||||
// This is a heads up animation
|
||||
return false;
|
||||
}
|
||||
finalState.applyState(child, viewState);
|
||||
return true;
|
||||
}
|
||||
|
||||
private int findLastNotAddedIndex(StackScrollState finalState) {
|
||||
int childCount = mHostLayout.getChildCount();
|
||||
for (int i = childCount - 1; i >= 0; i--) {
|
||||
@@ -619,7 +653,9 @@ public class StackStateAnimator {
|
||||
|
||||
ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Y,
|
||||
child.getTranslationY(), newEndValue);
|
||||
animator.setInterpolator(mFastOutSlowInInterpolator);
|
||||
Interpolator interpolator = mHeadsUpAppearChildren.contains(child) ?
|
||||
mHeadsUpAppearInterpolator :mFastOutSlowInInterpolator;
|
||||
animator.setInterpolator(interpolator);
|
||||
long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator);
|
||||
animator.setDuration(newDuration);
|
||||
if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) {
|
||||
@@ -836,8 +872,17 @@ public class StackStateAnimator {
|
||||
// This item is added, initialize it's properties.
|
||||
StackViewState viewState = finalState.getViewStateForView(changingView);
|
||||
mTmpState.copyFrom(viewState);
|
||||
mTmpState.yTranslation = -mTmpState.height;
|
||||
if (event.headsUpFromBottom) {
|
||||
mTmpState.yTranslation = mHeadsUpAppearHeightBottom;
|
||||
} else {
|
||||
mTmpState.yTranslation = -mTmpState.height;
|
||||
}
|
||||
mHeadsUpAppearChildren.add(changingView);
|
||||
finalState.applyState(changingView, mTmpState);
|
||||
} else if (event.animationType == NotificationStackScrollLayout
|
||||
.AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR) {
|
||||
// This item is added, initialize it's properties.
|
||||
mHeadsUpDisappearChildren.add(changingView);
|
||||
}
|
||||
mNewEvents.add(event);
|
||||
}
|
||||
@@ -903,4 +948,12 @@ public class StackStateAnimator {
|
||||
return getChildTag(view, TAG_END_HEIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
public void setHeadsUpAppearHeightBottom(int headsUpAppearHeightBottom) {
|
||||
mHeadsUpAppearHeightBottom = headsUpAppearHeightBottom;
|
||||
}
|
||||
|
||||
public void setShadeExpanded(boolean shadeExpanded) {
|
||||
mShadeExpanded = shadeExpanded;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user