diff options
author | Selim Cinek <cinek@google.com> | 2015-04-07 10:51:49 -0700 |
---|---|---|
committer | Selim Cinek <cinek@google.com> | 2015-04-15 12:50:12 -0700 |
commit | a59ecc3401de0c4bf1e13665158f54669f22d06c (patch) | |
tree | 0fd807011aa1fb73ea7c0ea5c408623c1817a533 /packages/SystemUI/src/com/android/systemui/statusbar | |
parent | b8f09cf5533d458868a335ce334e4880b2b0788d (diff) | |
download | frameworks_base-a59ecc3401de0c4bf1e13665158f54669f22d06c.zip frameworks_base-a59ecc3401de0c4bf1e13665158f54669f22d06c.tar.gz frameworks_base-a59ecc3401de0c4bf1e13665158f54669f22d06c.tar.bz2 |
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
Diffstat (limited to 'packages/SystemUI/src/com/android/systemui/statusbar')
10 files changed, 304 insertions, 135 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index b9b3388..dc0b0e4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -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); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 6fe609e..eacc436 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -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 diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java index 1b89b3f..b19535b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java @@ -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; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 74a26a8..ab4ac8b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -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) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java index 6e0bf8f..84a9f64 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java @@ -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); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java index d43af59..85ff59a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java @@ -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(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java index 824ba94..f2b971f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java @@ -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; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index cac1b8a..656f23a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -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]); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java index b0f287f..1a42f45 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java @@ -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); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java index 9640b84..b869c27 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java @@ -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; + } } |