diff options
Diffstat (limited to 'packages')
15 files changed, 557 insertions, 216 deletions
diff --git a/packages/Keyguard/res/layout/keyguard_pin_view.xml b/packages/Keyguard/res/layout/keyguard_pin_view.xml index a804c8c..a8e330b 100644 --- a/packages/Keyguard/res/layout/keyguard_pin_view.xml +++ b/packages/Keyguard/res/layout/keyguard_pin_view.xml @@ -41,28 +41,16 @@ android:layout_weight="1" android:layoutDirection="ltr" > - <LinearLayout + <RelativeLayout + android:id="@+id/row0" android:layout_width="match_parent" android:layout_height="0dp" - android:orientation="horizontal" android:layout_weight="1" > - <TextView android:id="@+id/pinEntry" - android:editable="true" - android:layout_width="0dip" - android:layout_height="match_parent" - android:layout_weight="1" - android:gravity="center" - android:layout_marginStart="@dimen/keyguard_lockscreen_pin_margin_left" - android:singleLine="true" - android:cursorVisible="false" - android:background="@null" - android:textAppearance="@style/TextAppearance.NumPadKey" - android:imeOptions="flagForceAscii|actionDone" - /> - <ImageButton android:id="@+id/delete_button" + <ImageButton android:id="@+id/delete_button" android:layout_width="wrap_content" android:layout_height="match_parent" + android:layout_alignParentEnd="true" android:gravity="center_vertical" android:src="@drawable/ic_input_delete" android:clickable="true" @@ -73,13 +61,30 @@ android:background="?android:attr/selectableItemBackground" android:contentDescription="@string/keyboardview_keycode_delete" /> - </LinearLayout> - <View - android:layout_width="wrap_content" - android:layout_height="1dp" - android:background="#55FFFFFF" - /> + <TextView android:id="@+id/pinEntry" + android:editable="true" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_toStartOf="@+id/delete_button" + android:layout_alignParentStart="true" + android:gravity="center" + android:layout_marginStart="@dimen/keyguard_lockscreen_pin_margin_left" + android:singleLine="true" + android:cursorVisible="false" + android:background="@null" + android:textAppearance="@style/TextAppearance.NumPadKey" + android:imeOptions="flagForceAscii|actionDone" + /> + <View + android:id="@+id/divider" + android:layout_width="match_parent" + android:layout_height="1dp" + android:layout_alignParentBottom="true" + android:background="#55FFFFFF" + /> + </RelativeLayout> <LinearLayout + android:id="@+id/row1" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" @@ -114,6 +119,7 @@ /> </LinearLayout> <LinearLayout + android:id="@+id/row2" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" @@ -148,6 +154,7 @@ /> </LinearLayout> <LinearLayout + android:id="@+id/row3" android:layout_width="match_parent" android:layout_height="0dp" android:orientation="horizontal" @@ -182,6 +189,7 @@ /> </LinearLayout> <LinearLayout + android:id="@+id/row4" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" diff --git a/packages/Keyguard/res/values/dimens.xml b/packages/Keyguard/res/values/dimens.xml index 3830df7..01d9ab3 100644 --- a/packages/Keyguard/res/values/dimens.xml +++ b/packages/Keyguard/res/values/dimens.xml @@ -161,4 +161,6 @@ <dimen name="widget_big_font_size">68dp</dimen> <dimen name="big_font_size">120dp</dimen> + <!-- The y translation to apply at the start in appear animations. --> + <dimen name="appear_y_translation_start">24dp</dimen> </resources> diff --git a/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java b/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java new file mode 100644 index 0000000..ea896d5 --- /dev/null +++ b/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.keyguard; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.TimeInterpolator; +import android.content.Context; +import android.view.View; +import android.view.ViewPropertyAnimator; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; + +/** + * A class to make nice appear transitions for views in a tabular layout. + */ +public class AppearAnimationUtils { + + public static final long APPEAR_DURATION = 220; + + private final Interpolator mLinearOutSlowIn; + private final float mStartTranslation; + + public AppearAnimationUtils(Context ctx) { + mLinearOutSlowIn = AnimationUtils.loadInterpolator( + ctx, android.R.interpolator.linear_out_slow_in); + mStartTranslation = + ctx.getResources().getDimensionPixelOffset(R.dimen.appear_y_translation_start); + } + + public void startAppearAnimation(View[][] views, final Runnable finishListener) { + long maxDelay = 0; + ViewPropertyAnimator maxDelayAnimator = null; + for (int row = 0; row < views.length; row++) { + View[] columns = views[row]; + for (int col = 0; col < columns.length; col++) { + long delay = calculateDelay(row, col); + ViewPropertyAnimator animator = startAppearAnimation(columns[col], delay); + if (animator != null && delay > maxDelay) { + maxDelay = delay; + maxDelayAnimator = animator; + } + } + } + if (maxDelayAnimator != null) { + maxDelayAnimator.setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + finishListener.run(); + } + }); + } else { + finishListener.run(); + } + } + + private ViewPropertyAnimator startAppearAnimation(View view, long delay) { + if (view == null) return null; + view.setAlpha(0f); + view.setTranslationY(mStartTranslation); + view.animate() + .alpha(1f) + .translationY(0) + .setInterpolator(mLinearOutSlowIn) + .setDuration(APPEAR_DURATION) + .setStartDelay(delay) + .setListener(null); + if (view.hasOverlappingRendering()) { + view.animate().withLayer(); + } + return view.animate(); + } + + private long calculateDelay(int row, int col) { + return (long) (row * 40 + col * (Math.pow(row, 0.4) + 0.4) * 20); + } + + public TimeInterpolator getInterpolator() { + return mLinearOutSlowIn; + } + + public float getStartTranslation() { + return mStartTranslation; + } +} diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java index 4dfda91..1f3c176 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java @@ -22,6 +22,7 @@ import android.text.TextWatcher; import android.text.method.DigitsKeyListener; import android.util.AttributeSet; import android.view.View; +import android.view.ViewGroup; import android.widget.TextView.OnEditorActionListener; /** @@ -30,12 +31,21 @@ import android.widget.TextView.OnEditorActionListener; public class KeyguardPINView extends KeyguardAbsKeyInputView implements KeyguardSecurityView, OnEditorActionListener, TextWatcher { + private final AppearAnimationUtils mAppearAnimationUtils; + private ViewGroup mKeyguardBouncerFrame; + private ViewGroup mRow0; + private ViewGroup mRow1; + private ViewGroup mRow2; + private ViewGroup mRow3; + private View mDivider; + public KeyguardPINView(Context context) { this(context, null); } public KeyguardPINView(Context context, AttributeSet attrs) { super(context, attrs); + mAppearAnimationUtils = new AppearAnimationUtils(context); } protected void resetState() { @@ -56,6 +66,12 @@ public class KeyguardPINView extends KeyguardAbsKeyInputView protected void onFinishInflate() { super.onFinishInflate(); + mKeyguardBouncerFrame = (ViewGroup) findViewById(R.id.keyguard_bouncer_frame); + mRow0 = (ViewGroup) findViewById(R.id.row0); + mRow1 = (ViewGroup) findViewById(R.id.row1); + mRow2 = (ViewGroup) findViewById(R.id.row2); + mRow3 = (ViewGroup) findViewById(R.id.row3); + mDivider = findViewById(R.id.divider); final View ok = findViewById(R.id.key_enter); if (ok != null) { ok.setOnClickListener(new View.OnClickListener() { @@ -117,8 +133,45 @@ public class KeyguardPINView extends KeyguardAbsKeyInputView @Override public void startAppearAnimation() { - // TODO: Fancy animation. - setAlpha(0); - animate().alpha(1).withLayer().setDuration(200); + enableClipping(false); + setTranslationY(mAppearAnimationUtils.getStartTranslation()); + animate() + .setDuration(500) + .setInterpolator(mAppearAnimationUtils.getInterpolator()) + .translationY(0); + mAppearAnimationUtils.startAppearAnimation(new View[][] { + new View[] { + mRow0, null, null + }, + new View[] { + findViewById(R.id.key1), findViewById(R.id.key2), findViewById(R.id.key3) + }, + new View[] { + findViewById(R.id.key4), findViewById(R.id.key5), findViewById(R.id.key6) + }, + new View[] { + findViewById(R.id.key7), findViewById(R.id.key8), findViewById(R.id.key9) + }, + new View[] { + null, findViewById(R.id.key0), findViewById(R.id.key_enter) + }, + new View[] { + null, mEcaView, null + }}, + new Runnable() { + @Override + public void run() { + enableClipping(true); + } + }); + } + + private void enableClipping(boolean enable) { + mKeyguardBouncerFrame.setClipToPadding(enable); + mKeyguardBouncerFrame.setClipChildren(enable); + mRow1.setClipToPadding(enable); + mRow2.setClipToPadding(enable); + mRow3.setClipToPadding(enable); + setClipChildren(enable); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java index 130f9ce..267786b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java @@ -85,7 +85,7 @@ public class NotificationsTile extends QSTile<NotificationsTile.NotificationsSta // noop } }); - vp.postVolumeChanged(AudioManager.STREAM_NOTIFICATION, AudioManager.FLAG_SHOW_UI); + vp.postVolumeChanged(AudioManager.STREAM_RING, AudioManager.FLAG_SHOW_UI); return v; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index f4db625..fbe76f9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -48,6 +48,7 @@ import android.provider.Settings; import android.service.dreams.DreamService; import android.service.dreams.IDreamManager; import android.service.notification.NotificationListenerService; +import android.service.notification.NotificationListenerService.Ranking; import android.service.notification.StatusBarNotification; import android.text.TextUtils; import android.util.Log; @@ -77,11 +78,12 @@ import com.android.systemui.R; import com.android.systemui.RecentsComponent; import com.android.systemui.SearchPanelView; import com.android.systemui.SystemUI; +import com.android.systemui.statusbar.NotificationData.Entry; import com.android.systemui.statusbar.phone.KeyguardTouchDelegate; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collections; import java.util.Locale; import static com.android.keyguard.KeyguardHostView.OnDismissAction; @@ -91,7 +93,7 @@ public abstract class BaseStatusBar extends SystemUI implements public static final String TAG = "StatusBar"; public static final boolean DEBUG = false; public static final boolean MULTIUSER_DEBUG = false; - private static final boolean USE_NOTIFICATION_LISTENER = false; + private static final boolean USE_NOTIFICATION_LISTENER = true; protected static final int MSG_SHOW_RECENT_APPS = 1019; protected static final int MSG_HIDE_RECENT_APPS = 1020; @@ -194,7 +196,7 @@ public abstract class BaseStatusBar extends SystemUI implements mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0); if (provisioned != mDeviceProvisioned) { mDeviceProvisioned = provisioned; - updateNotificationIcons(); + updateNotifications(); } final int mode = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF); @@ -209,7 +211,7 @@ public abstract class BaseStatusBar extends SystemUI implements // so we just dump our cache ... mUsersAllowingPrivateNotifications.clear(); // ... and refresh all the notifications - updateNotificationIcons(); + updateNotifications(); } }; @@ -280,11 +282,12 @@ public abstract class BaseStatusBar extends SystemUI implements public void onListenerConnected() { if (DEBUG) Log.d(TAG, "onListenerConnected"); final StatusBarNotification[] notifications = getActiveNotifications(); + final Ranking currentRanking = getCurrentRanking(); mHandler.post(new Runnable() { @Override public void run() { for (StatusBarNotification sbn : notifications) { - addNotificationInternal(sbn); + addNotificationInternal(sbn, currentRanking); } } }); @@ -293,13 +296,14 @@ public abstract class BaseStatusBar extends SystemUI implements @Override public void onNotificationPosted(final StatusBarNotification sbn) { if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn); + final Ranking currentRanking = getCurrentRanking(); mHandler.post(new Runnable() { @Override public void run() { if (mNotificationData.findByKey(sbn.getKey()) != null) { - updateNotificationInternal(sbn); + updateNotificationInternal(sbn, currentRanking); } else { - addNotificationInternal(sbn); + addNotificationInternal(sbn, currentRanking); } } }); @@ -308,10 +312,24 @@ public abstract class BaseStatusBar extends SystemUI implements @Override public void onNotificationRemoved(final StatusBarNotification sbn) { if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn); + final Ranking currentRanking = getCurrentRanking(); mHandler.post(new Runnable() { @Override public void run() { - removeNotificationInternal(sbn.getKey()); + removeNotificationInternal(sbn.getKey(), currentRanking); + } + }); + } + + @Override + public void onNotificationRankingUpdate() { + if (DEBUG) Log.d(TAG, "onRankingUpdate"); + final Ranking currentRanking = getCurrentRanking(); + mHandler.post(new Runnable() { + @Override + public void run() { + mNotificationData.updateRanking(currentRanking); + updateNotifications(); } }); } @@ -1113,19 +1131,13 @@ public abstract class BaseStatusBar extends SystemUI implements } } - protected StatusBarNotification removeNotificationViews(String key) { - NotificationData.Entry entry = mNotificationData.remove(key); + protected StatusBarNotification removeNotificationViews(String key, Ranking ranking) { + NotificationData.Entry entry = mNotificationData.remove(key, ranking); if (entry == null) { Log.w(TAG, "removeNotification for unknown key: " + key); return null; } - // Remove the expanded view. - ViewGroup rowParent = (ViewGroup)entry.row.getParent(); - if (rowParent != null) rowParent.removeView(entry.row); - updateRowStates(); - updateNotificationIcons(); - updateSpeedBump(); - + updateNotifications(); return entry.notification; } @@ -1159,35 +1171,17 @@ public abstract class BaseStatusBar extends SystemUI implements return entry; } - protected void addNotificationViews(NotificationData.Entry entry) { + protected void addNotificationViews(Entry entry, Ranking ranking) { if (entry == null) { return; } // Add the expanded view and icon. - int pos = mNotificationData.add(entry); - if (DEBUG) { - Log.d(TAG, "addNotificationViews: added at " + pos); - } - updateRowStates(); - updateNotificationIcons(); - updateSpeedBump(); - } - - protected void updateSpeedBump() { - int n = mNotificationData.size(); - int speedBumpIndex = -1; - for (int i = n-1; i >= 0; i--) { - NotificationData.Entry entry = mNotificationData.get(i); - if (entry.row.getVisibility() != View.GONE && speedBumpIndex == -1 - && entry.row.isBelowSpeedBump() ) { - speedBumpIndex = n - 1 - i; - } - } - mStackScroller.updateSpeedBumpIndex(speedBumpIndex); + mNotificationData.add(entry, ranking); + updateNotifications(); } - private void addNotificationViews(StatusBarNotification notification) { - addNotificationViews(createNotificationViews(notification)); + private void addNotificationViews(StatusBarNotification notification, Ranking ranking) { + addNotificationViews(createNotificationViews(notification), ranking); } /** @@ -1201,17 +1195,17 @@ public abstract class BaseStatusBar extends SystemUI implements protected void updateRowStates() { int maxKeyguardNotifications = getMaxKeyguardNotifications(); mKeyguardIconOverflowContainer.getIconsView().removeAllViews(); - int n = mNotificationData.size(); + final int N = mNotificationData.size(); int visibleNotifications = 0; boolean onKeyguard = mState == StatusBarState.KEYGUARD; - for (int i = n-1; i >= 0; i--) { + for (int i = 0; i < N; i++) { NotificationData.Entry entry = mNotificationData.get(i); if (onKeyguard) { entry.row.setExpansionDisabled(true); } else { entry.row.setExpansionDisabled(false); if (!entry.row.isUserLocked()) { - boolean top = (i == n-1); + boolean top = (i == 0); entry.row.setSystemExpanded(top); } } @@ -1238,6 +1232,9 @@ public abstract class BaseStatusBar extends SystemUI implements } else { mKeyguardIconOverflowContainer.setVisibility(View.GONE); } + // Move overflow container to last position. + mStackScroller.changeViewPosition(mKeyguardIconOverflowContainer, + mStackScroller.getChildCount() - 1); } private boolean shouldShowOnKeyguard(StatusBarNotification sbn) { @@ -1247,46 +1244,42 @@ public abstract class BaseStatusBar extends SystemUI implements protected void setZenMode(int mode) { if (!isDeviceProvisioned()) return; mZenMode = mode; - updateNotificationIcons(); + updateNotifications(); } protected abstract void haltTicker(); protected abstract void setAreThereNotifications(); - protected abstract void updateNotificationIcons(); + protected abstract void updateNotifications(); protected abstract void tick(StatusBarNotification n, boolean firstTime); protected abstract void updateExpandedViewPos(int expandedPosition); protected abstract boolean shouldDisableNavbarGestures(); - protected boolean isTopNotification(ViewGroup parent, NotificationData.Entry entry) { - return parent != null && parent.indexOfChild(entry.row) == 0; - } - - @Override public void addNotification(StatusBarNotification notification) { if (!USE_NOTIFICATION_LISTENER) { - addNotificationInternal(notification); + addNotificationInternal(notification, null); } } - public abstract void addNotificationInternal(StatusBarNotification notification); + public abstract void addNotificationInternal(StatusBarNotification notification, + Ranking ranking); @Override public void removeNotification(String key) { if (!USE_NOTIFICATION_LISTENER) { - removeNotificationInternal(key); + removeNotificationInternal(key, null); } } - protected abstract void removeNotificationInternal(String key); + protected abstract void removeNotificationInternal(String key, Ranking ranking); public void updateNotification(StatusBarNotification notification) { if (!USE_NOTIFICATION_LISTENER) { - updateNotificationInternal(notification); + updateNotificationInternal(notification, null); } } - public void updateNotificationInternal(StatusBarNotification notification) { + public void updateNotificationInternal(StatusBarNotification notification, Ranking ranking) { if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")"); final NotificationData.Entry oldEntry = mNotificationData.findByKey(notification.getKey()); @@ -1358,18 +1351,12 @@ public abstract class BaseStatusBar extends SystemUI implements && oldPublicContentView.getPackage().equals(publicContentView.getPackage()) && oldPublicContentView.getLayoutId() == publicContentView.getLayoutId()); - ViewGroup rowParent = (ViewGroup) oldEntry.row.getParent(); - boolean orderUnchanged = - notification.getNotification().when == oldNotification.getNotification().when - && notification.getScore() == oldNotification.getScore(); - // score now encompasses/supersedes isOngoing() boolean updateTicker = notification.getNotification().tickerText != null && !TextUtils.equals(notification.getNotification().tickerText, oldEntry.notification.getNotification().tickerText); - boolean isTopAnyway = isTopNotification(rowParent, oldEntry); - if (contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged && publicUnchanged - && (orderUnchanged || isTopAnyway)) { + if (contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged + && publicUnchanged) { if (DEBUG) Log.d(TAG, "reusing notification for key: " + notification.getKey()); oldEntry.notification = notification; try { @@ -1397,22 +1384,20 @@ public abstract class BaseStatusBar extends SystemUI implements handleNotificationError(notification, "Couldn't update icon: " + ic); return; } - updateRowStates(); - updateSpeedBump(); + mNotificationData.updateRanking(ranking); + updateNotifications(); } catch (RuntimeException e) { // It failed to add cleanly. Log, and remove the view from the panel. Log.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e); - removeNotificationViews(notification.getKey()); - addNotificationViews(notification); + removeNotificationViews(notification.getKey(), ranking); + addNotificationViews(notification, ranking); } } else { if (DEBUG) Log.d(TAG, "not reusing notification for key: " + notification.getKey()); if (DEBUG) Log.d(TAG, "contents was " + (contentsUnchanged ? "unchanged" : "changed")); - if (DEBUG) Log.d(TAG, "order was " + (orderUnchanged ? "unchanged" : "changed")); - if (DEBUG) Log.d(TAG, "notification is " + (isTopAnyway ? "top" : "not top")); - removeNotificationViews(notification.getKey()); - addNotificationViews(notification); // will also replace the heads up + removeNotificationViews(notification.getKey(), ranking); + addNotificationViews(notification, ranking); // will also replace the heads up final NotificationData.Entry newEntry = mNotificationData.findByKey( notification.getKey()); final boolean userChangedExpansion = oldEntry.row.hasUserChangedExpansion(); @@ -1559,5 +1544,12 @@ public abstract class BaseStatusBar extends SystemUI implements mWindowManager.removeViewImmediate(mSearchPanelView); } mContext.unregisterReceiver(mBroadcastReceiver); + if (USE_NOTIFICATION_LISTENER) { + try { + mNotificationListener.unregisterAsSystemService(); + } catch (RemoteException e) { + // Ignore. + } + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java index 0555879..24da5c2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java @@ -31,7 +31,6 @@ import com.android.systemui.statusbar.phone.PhoneStatusBar; public class InterceptedNotifications { private static final String TAG = "InterceptedNotifications"; private static final String EXTRA_INTERCEPT = "android.intercept"; - private static final String SYNTHETIC_KEY = "InterceptedNotifications.SYNTHETIC_KEY"; private final Context mContext; private final PhoneStatusBar mBar; @@ -50,7 +49,7 @@ public class InterceptedNotifications { for (int i = 0; i < n; i++) { final StatusBarNotification sbn = mIntercepted.valueAt(i); sbn.getNotification().extras.putBoolean(EXTRA_INTERCEPT, false); - mBar.addNotificationInternal(sbn); + mBar.addNotificationInternal(sbn, null); } mIntercepted.clear(); updateSyntheticNotification(); @@ -71,7 +70,7 @@ public class InterceptedNotifications { } public boolean isSyntheticEntry(Entry ent) { - return ent.key.equals(SYNTHETIC_KEY); + return ent.key.equals(mSynKey); } public void update(StatusBarNotification notification) { @@ -88,7 +87,7 @@ public class InterceptedNotifications { private void updateSyntheticNotification() { if (mIntercepted.isEmpty()) { if (mSynKey != null) { - mBar.removeNotificationInternal(mSynKey); + mBar.removeNotificationInternal(mSynKey, null); mSynKey = null; } return; @@ -107,9 +106,9 @@ public class InterceptedNotifications { mBar.getCurrentUserHandle()); if (mSynKey == null) { mSynKey = sbn.getKey(); - mBar.addNotificationInternal(sbn); + mBar.addNotificationInternal(sbn, null); } else { - mBar.updateNotificationInternal(sbn); + mBar.updateNotificationInternal(sbn, null); } final NotificationData.Entry entry = mBar.mNotificationData.findByKey(mSynKey); entry.row.setOnClickListener(mSynClickListener); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java index 5696246..d829ac0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java @@ -16,15 +16,19 @@ package com.android.systemui.statusbar; +import android.app.Notification; +import android.service.notification.NotificationListenerService.Ranking; import android.service.notification.StatusBarNotification; import android.view.View; -import android.widget.ImageView; import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; /** * The list of currently displaying notifications. + * + * TODO: Rename to NotificationList. */ public class NotificationData { public static final class Entry { @@ -34,7 +38,6 @@ public class NotificationData { public ExpandableNotificationRow row; // the outer expanded view public View expanded; // the inflated RemoteViews public View expandedPublic; // for insecure lockscreens - public ImageView largeIcon; public View expandedBig; private boolean interruption; public Entry() {} @@ -64,18 +67,23 @@ public class NotificationData { } private final ArrayList<Entry> mEntries = new ArrayList<Entry>(); - private final Comparator<Entry> mEntryCmp = new Comparator<Entry>() { - // sort first by score, then by when + private Ranking mRanking; + private final Comparator<Entry> mRankingComparator = new Comparator<Entry>() { + @Override public int compare(Entry a, Entry b) { + if (mRanking != null) { + return mRanking.getRank(a.key) - mRanking.getRank(b.key); + } + final StatusBarNotification na = a.notification; final StatusBarNotification nb = b.notification; - int d = na.getScore() - nb.getScore(); + int d = nb.getScore() - na.getScore(); if (a.interruption != b.interruption) { - return a.interruption ? 1 : -1; + return a.interruption ? -1 : 1; } else if (d != 0) { return d; } else { - return (int) (na.getNotification().when - nb.getNotification().when); + return (int) (nb.getNotification().when - na.getNotification().when); } } }; @@ -97,26 +105,47 @@ public class NotificationData { return null; } - public int add(Entry entry) { - int i; - int N = mEntries.size(); - for (i = 0; i < N; i++) { - if (mEntryCmp.compare(mEntries.get(i), entry) > 0) { - break; - } - } - mEntries.add(i, entry); - return i; + public void add(Entry entry, Ranking ranking) { + mEntries.add(entry); + updateRankingAndSort(ranking); } - public Entry remove(String key) { + public Entry remove(String key, Ranking ranking) { Entry e = findByKey(key); - if (e != null) { - mEntries.remove(e); + if (e == null) { + return null; } + mEntries.remove(e); + updateRankingAndSort(ranking); return e; } + public void updateRanking(Ranking ranking) { + updateRankingAndSort(ranking); + } + + public boolean isAmbient(String key) { + // TODO: Remove when switching to NotificationListener. + if (mRanking == null) { + for (Entry entry : mEntries) { + if (key.equals(entry.key)) { + return entry.notification.getNotification().priority == + Notification.PRIORITY_MIN; + } + } + } else { + return mRanking.isAmbient(key); + } + return false; + } + + private void updateRankingAndSort(Ranking ranking) { + if (ranking != null) { + mRanking = ranking; + } + Collections.sort(mEntries, mRankingComparator); + } + /** * Return whether there are any visible items (i.e. items without an error). */ 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 c61392a..5dcd61c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -64,6 +64,7 @@ import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; import android.provider.Settings.Global; +import android.service.notification.NotificationListenerService.Ranking; import android.service.notification.StatusBarNotification; import android.util.ArraySet; import android.util.DisplayMetrics; @@ -1037,13 +1038,15 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } @Override - public void addNotificationInternal(StatusBarNotification notification) { + public void addNotificationInternal(StatusBarNotification notification, Ranking ranking) { if (DEBUG) Log.d(TAG, "addNotification score=" + notification.getScore()); Entry shadeEntry = createNotificationViews(notification); if (shadeEntry == null) { return; } if (mZenMode != Global.ZEN_MODE_OFF && mIntercepted.tryIntercept(notification)) { + // Forward the ranking so we can sort the new notification. + mNotificationData.updateRanking(ranking); return; } if (mUseHeadsUp && shouldInterrupt(notification)) { @@ -1083,7 +1086,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, tick(notification, true); } } - addNotificationViews(shadeEntry); + addNotificationViews(shadeEntry, ranking); // Recalculate the position of the sliding windows and the titles. setAreThereNotifications(); updateExpandedViewPos(EXPANDED_LEAVE_ALONE); @@ -1099,14 +1102,14 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } @Override - public void updateNotification(StatusBarNotification notification) { - super.updateNotification(notification); + public void updateNotificationInternal(StatusBarNotification notification, Ranking ranking) { + super.updateNotificationInternal(notification, ranking); mIntercepted.update(notification); } @Override - public void removeNotificationInternal(String key) { - StatusBarNotification old = removeNotificationViews(key); + public void removeNotificationInternal(String key, Ranking ranking) { + StatusBarNotification old = removeNotificationViews(key, ranking); if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old); if (old != null) { @@ -1143,7 +1146,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, R.integer.config_show_search_delay); } - private void loadNotificationShade() { + private void updateNotificationShade() { if (mStackScroller == null) return; int N = mNotificationData.size(); @@ -1153,7 +1156,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, final boolean provisioned = isDeviceProvisioned(); // If the device hasn't been through Setup, we only show system notifications for (int i=0; i<N; i++) { - Entry ent = mNotificationData.get(N-i-1); + Entry ent = mNotificationData.get(i); if (!(provisioned || showNotificationEvenIfUnprovisioned(ent.notification))) continue; // TODO How do we want to badge notifcations from profiles. @@ -1184,26 +1187,75 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, for (int i=0; i<toShow.size(); i++) { View v = toShow.get(i); if (v.getParent() == null) { - mStackScroller.addView(v, i); + mStackScroller.addView(v); } } + // So after all this work notifications still aren't sorted correctly. + // Let's do that now by advancing through toShow and mStackScroller in + // lock-step, making sure mStackScroller matches what we see in toShow. + int j = 0; + for (int i = 0; i < mStackScroller.getChildCount(); i++) { + View child = mStackScroller.getChildAt(i); + if (!(child instanceof ExpandableNotificationRow)) { + // We don't care about non-notification views. + continue; + } + + if (child == toShow.get(j)) { + // Everything is well, advance both lists. + j++; + continue; + } + + // Oops, wrong notification at this position. Put the right one + // here and advance both lists. + mStackScroller.changeViewPosition(toShow.get(j), i); + j++; + } + updateRowStates(); + updateSpeedbump(); mNotificationPanel.setQsExpansionEnabled(provisioned && mUserSetup); } + private void updateSpeedbump() { + int speedbumpIndex = -1; + int currentIndex = 0; + for (int i = 0; i < mNotificationData.size(); i++) { + Entry entry = mNotificationData.get(i); + if (entry.row.getParent() == null) { + // This view isn't even added, so the stack scroller doesn't + // know about it. Ignore completely. + continue; + } + if (entry.row.getVisibility() != View.GONE && + mNotificationData.isAmbient(entry.key)) { + speedbumpIndex = currentIndex; + break; + } + currentIndex++; + } + mStackScroller.updateSpeedBumpIndex(speedbumpIndex); + } + @Override - protected void updateNotificationIcons() { + protected void updateNotifications() { + // TODO: Move this into updateNotificationIcons()? if (mNotificationIcons == null) return; - loadNotificationShade(); + updateNotificationShade(); + updateNotificationIcons(); + } + private void updateNotificationIcons() { final LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mNaturalBarHeight); int N = mNotificationData.size(); if (DEBUG) { - Log.d(TAG, "refreshing icons: " + N + " notifications, mNotificationIcons=" + mNotificationIcons); + Log.d(TAG, "refreshing icons: " + N + " notifications, mNotificationIcons=" + + mNotificationIcons); } ArrayList<View> toShow = new ArrayList<View>(); @@ -1211,7 +1263,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, final boolean provisioned = isDeviceProvisioned(); // If the device hasn't been through Setup, we only show system notifications for (int i=0; i<N; i++) { - Entry ent = mNotificationData.get(N-i-1); + Entry ent = mNotificationData.get(i); if (!((provisioned && ent.notification.getScore() >= HIDE_ICONS_BELOW_SCORE) || showNotificationEvenIfUnprovisioned(ent.notification))) continue; if (!notificationIsForCurrentProfiles(ent.notification)) continue; @@ -2388,7 +2440,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, public void userSwitched(int newUserId) { if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId); animateCollapsePanels(); - updateNotificationIcons(); + updateNotifications(); resetUserSetupObserver(); } @@ -2793,10 +2845,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, updateStackScrollerState(); updatePublicMode(); - updateRowStates(); - updateSpeedBump(); + updateNotifications(); checkBarModes(); - updateNotificationIcons(); updateCarrierLabelVisibility(false); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java index 5e2d06b..cf56fa57 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java @@ -27,6 +27,7 @@ public class AnimationFilter { boolean animateZ; boolean animateScale; boolean animateHeight; + boolean animateTopInset; boolean animateDimmed; boolean hasDelays; @@ -60,6 +61,11 @@ public class AnimationFilter { return this; } + public AnimationFilter animateTopInset() { + animateTopInset = true; + return this; + } + public AnimationFilter animateDimmed() { animateDimmed = true; return this; @@ -84,6 +90,7 @@ public class AnimationFilter { animateZ |= filter.animateZ; animateScale |= filter.animateScale; animateHeight |= filter.animateHeight; + animateTopInset |= filter.animateTopInset; animateDimmed |= filter.animateDimmed; hasDelays |= filter.hasDelays; } @@ -94,6 +101,7 @@ public class AnimationFilter { animateZ = false; animateScale = false; animateHeight = false; + animateTopInset = false; animateDimmed = false; hasDelays = false; } 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 079b184..58176b9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -1599,6 +1599,7 @@ public class NotificationStackScrollLayout extends ViewGroup new AnimationFilter() .animateAlpha() .animateHeight() + .animateTopInset() .animateY() .animateZ() .hasDelays(), @@ -1607,6 +1608,7 @@ public class NotificationStackScrollLayout extends ViewGroup new AnimationFilter() .animateAlpha() .animateHeight() + .animateTopInset() .animateY() .animateZ() .hasDelays(), @@ -1615,6 +1617,7 @@ public class NotificationStackScrollLayout extends ViewGroup new AnimationFilter() .animateAlpha() .animateHeight() + .animateTopInset() .animateY() .animateZ() .hasDelays(), @@ -1623,6 +1626,7 @@ public class NotificationStackScrollLayout extends ViewGroup new AnimationFilter() .animateAlpha() .animateHeight() + .animateTopInset() .animateY() .animateDimmed() .animateScale() @@ -1651,6 +1655,7 @@ public class NotificationStackScrollLayout extends ViewGroup new AnimationFilter() .animateAlpha() .animateHeight() + .animateTopInset() .animateY() .animateZ() }; 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 bd2541a..2b52c7e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java @@ -49,6 +49,7 @@ public class StackScrollAlgorithm { private int mBottomStackPeekSize; private int mZDistanceBetweenElements; private int mZBasicHeight; + private int mRoundedRectCornerRadius; private StackIndentationFunctor mTopStackIndentationFunctor; private StackIndentationFunctor mBottomStackIndentationFunctor; @@ -111,6 +112,8 @@ public class StackScrollAlgorithm { mZBasicHeight = (MAX_ITEMS_IN_BOTTOM_STACK + 1) * mZDistanceBetweenElements; mBottomStackSlowDownLength = context.getResources() .getDimensionPixelSize(R.dimen.bottom_stack_slow_down_length); + mRoundedRectCornerRadius = context.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.notification_quantum_rounded_rect_radius); } @@ -146,6 +149,67 @@ public class StackScrollAlgorithm { handleDraggedViews(ambientState, resultState, algorithmState); updateDimmedActivated(ambientState, resultState, algorithmState); + updateClipping(resultState, algorithmState); + } + + private void updateClipping(StackScrollState resultState, + StackScrollAlgorithmState algorithmState) { + float previousNotificationEnd = 0; + float previousNotificationStart = 0; + boolean previousNotificationIsSwiped = false; + int childCount = algorithmState.visibleChildren.size(); + for (int i = 0; i < childCount; i++) { + ExpandableView child = algorithmState.visibleChildren.get(i); + StackScrollState.ViewState state = resultState.getViewStateForView(child); + float newYTranslation = state.yTranslation; + int newHeight = state.height; + // apply clipping and shadow + float newNotificationEnd = newYTranslation + newHeight; + + // In the unlocked shade we have to clip a little bit higher because of the rounded + // corners of the notifications. + float clippingCorrection = state.dimmed ? 0 : mRoundedRectCornerRadius; + + // When the previous notification is swiped, we don't clip the content to the + // bottom of it. + float clipHeight = previousNotificationIsSwiped + ? newHeight + : newNotificationEnd - (previousNotificationEnd - clippingCorrection); + + updateChildClippingAndBackground(state, newHeight, clipHeight, + (int) (newHeight - (previousNotificationStart - newYTranslation))); + + if (!child.isTransparent()) { + // Only update the previous values if we are not transparent, + // otherwise we would clip to a transparent view. + previousNotificationStart = newYTranslation + child.getClipTopAmount(); + previousNotificationEnd = newNotificationEnd; + previousNotificationIsSwiped = child.getTranslationX() != 0; + } + } + } + + /** + * Updates the shadow outline and the clipping for a view. + * + * @param state the viewState to update + * @param realHeight the currently applied height of the view + * @param clipHeight the desired clip height, the rest of the view will be clipped from the top + * @param backgroundHeight the desired background height. The shadows of the view will be + * based on this height and the content will be clipped from the top + */ + private void updateChildClippingAndBackground(StackScrollState.ViewState state, int realHeight, + float clipHeight, int backgroundHeight) { + if (realHeight > clipHeight) { + state.topOverLap = (int) (realHeight - clipHeight); + } else { + state.topOverLap = 0; + } + if (realHeight > backgroundHeight) { + state.clipTopAmount = (realHeight - backgroundHeight); + } else { + state.clipTopAmount = 0; + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java index 44e10be..94cb16d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.stack; -import android.graphics.Outline; import android.graphics.Rect; import android.util.Log; import android.view.View; @@ -37,15 +36,12 @@ public class StackScrollState { private static final String CHILD_NOT_FOUND_TAG = "StackScrollStateNoSuchChild"; private final ViewGroup mHostView; - private final int mRoundedRectCornerRadius; private Map<ExpandableView, ViewState> mStateMap; private final Rect mClipRect = new Rect(); public StackScrollState(ViewGroup hostView) { mHostView = hostView; mStateMap = new HashMap<ExpandableView, ViewState>(); - mRoundedRectCornerRadius = mHostView.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.notification_quantum_rounded_rect_radius); } public ViewGroup getHostView() { @@ -83,9 +79,6 @@ public class StackScrollState { */ public void apply() { int numChildren = mHostView.getChildCount(); - float previousNotificationEnd = 0; - float previousNotificationStart = 0; - boolean previousNotificationIsSwiped = false; for (int i = 0; i < numChildren; i++) { ExpandableView child = (ExpandableView) mHostView.getChildAt(i); ViewState state = mStateMap.get(child); @@ -155,39 +148,41 @@ public class StackScrollState { // apply dimming child.setDimmed(state.dimmed, false /* animate */); - // apply clipping and shadow - float newNotificationEnd = newYTranslation + newHeight; - - // In the unlocked shade we have to clip a little bit higher because of the rounded - // corners of the notifications. - float clippingCorrection = state.dimmed ? 0 : mRoundedRectCornerRadius; - - // When the previous notification is swiped, we don't clip the content to the - // bottom of it. - float clipHeight = previousNotificationIsSwiped - ? newHeight - : newNotificationEnd - (previousNotificationEnd - clippingCorrection); - - updateChildClippingAndBackground(child, newHeight, - clipHeight, - (int) (newHeight - (previousNotificationStart - newYTranslation))); - - if (!child.isTransparent()) { - // Only update the previous values if we are not transparent, - // otherwise we would clip to a transparent view. - previousNotificationStart = newYTranslation + child.getClipTopAmount(); - previousNotificationEnd = newNotificationEnd; - previousNotificationIsSwiped = child.getTranslationX() != 0; + float oldClipTopAmount = child.getClipTopAmount(); + if (oldClipTopAmount != state.clipTopAmount) { + child.setClipTopAmount(state.clipTopAmount); + } + + if (state.topOverLap != 0) { + updateChildClip(child, newHeight, state.topOverLap); + } else { + child.setClipBounds(null); } if(child instanceof SpeedBumpView) { - performSpeedBumpAnimation(i, (SpeedBumpView) child, newNotificationEnd, + float speedBumpEnd = newYTranslation + newHeight; + performSpeedBumpAnimation(i, (SpeedBumpView) child, speedBumpEnd, newYTranslation); } } } } + /** + * Updates the clipping of a view + * + * @param child the view to update + * @param height the currently applied height of the view + * @param clipInset how much should this view be clipped from the top + */ + private void updateChildClip(View child, int height, int clipInset) { + mClipRect.set(0, + clipInset, + child.getWidth(), + height); + child.setClipBounds(mClipRect); + } + private void performSpeedBumpAnimation(int i, SpeedBumpView speedBump, float speedBumpEnd, float speedBumpStart) { View nextChild = getNextChildNotGone(i); @@ -216,45 +211,6 @@ public class StackScrollState { return null; } - /** - * Updates the shadow outline and the clipping for a view. - * - * @param child the view to update - * @param realHeight the currently applied height of the view - * @param clipHeight the desired clip height, the rest of the view will be clipped from the top - * @param backgroundHeight the desired background height. The shadows of the view will be - * based on this height and the content will be clipped from the top - */ - private void updateChildClippingAndBackground(ExpandableView child, int realHeight, - float clipHeight, int backgroundHeight) { - if (realHeight > clipHeight) { - updateChildClip(child, realHeight, clipHeight); - } else { - child.setClipBounds(null); - } - if (realHeight > backgroundHeight) { - child.setClipTopAmount(realHeight - backgroundHeight); - } else { - child.setClipTopAmount(0); - } - } - - /** - * Updates the clipping of a view - * - * @param child the view to update - * @param height the currently applied height of the view - * @param clipHeight the desired clip height, the rest of the view will be clipped from the top - */ - private void updateChildClip(View child, int height, float clipHeight) { - int clipInset = (int) (height - clipHeight); - mClipRect.set(0, - clipInset, - child.getWidth(), - height); - child.setClipBounds(mClipRect); - } - public static class ViewState { // These are flags such that we can create masks for filtering. @@ -276,6 +232,18 @@ public class StackScrollState { boolean dimmed; /** + * The amount which the view should be clipped from the top. This is calculated to + * perceive consistent shadows. + */ + int clipTopAmount; + + /** + * How much does the child overlap with the previous view on the top? Can be used for + * a clipping optimization + */ + int topOverLap; + + /** * The index of the view, only accounting for views not equal to GONE */ int notGoneIndex; 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 f019e6c..f41ab3a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java @@ -133,10 +133,11 @@ public class StackStateAnimator { boolean scaleChanging = child.getScaleX() != viewState.scale; boolean alphaChanging = alpha != child.getAlpha(); boolean heightChanging = viewState.height != child.getActualHeight(); + boolean topInsetChanging = viewState.clipTopAmount != child.getClipTopAmount(); boolean wasAdded = mNewAddChildren.contains(child); boolean hasDelays = mAnimationFilter.hasDelays; boolean isDelayRelevant = yTranslationChanging || zTranslationChanging || scaleChanging || - alphaChanging || heightChanging; + alphaChanging || heightChanging || topInsetChanging; long delay = 0; if (hasDelays && isDelayRelevant || wasAdded) { delay = calculateChildAnimationDelay(viewState, finalState); @@ -167,6 +168,11 @@ public class StackStateAnimator { startHeightAnimation(child, viewState, delay); } + // start top inset animation + if (topInsetChanging) { + startInsetAnimation(child, viewState, delay); + } + // start dimmed animation child.setDimmed(viewState.dimmed, mAnimationFilter.animateDimmed); @@ -280,6 +286,64 @@ public class StackStateAnimator { child.setTag(TAG_END_HEIGHT, newEndValue); } + private void startInsetAnimation(final ExpandableView child, + StackScrollState.ViewState viewState, long delay) { + Integer previousStartValue = getChildTag(child, TAG_START_TOP_INSET); + Integer previousEndValue = getChildTag(child, TAG_END_TOP_INSET); + int newEndValue = viewState.clipTopAmount; + if (previousEndValue != null && previousEndValue == newEndValue) { + return; + } + ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TOP_INSET); + if (!mAnimationFilter.animateTopInset) { + // just a local update was performed + if (previousAnimator != null) { + // we need to increase all animation keyframes of the previous animator by the + // relative change to the end value + PropertyValuesHolder[] values = previousAnimator.getValues(); + int relativeDiff = newEndValue - previousEndValue; + int newStartValue = previousStartValue + relativeDiff; + values[0].setIntValues(newStartValue, newEndValue); + child.setTag(TAG_START_TOP_INSET, newStartValue); + child.setTag(TAG_END_TOP_INSET, newEndValue); + previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); + return; + } else { + // no new animation needed, let's just apply the value + child.setClipTopAmount(newEndValue); + return; + } + } + + ValueAnimator animator = ValueAnimator.ofInt(child.getClipTopAmount(), newEndValue); + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + child.setClipTopAmount((int) animation.getAnimatedValue()); + } + }); + animator.setInterpolator(mFastOutSlowInInterpolator); + long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator); + animator.setDuration(newDuration); + if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) { + animator.setStartDelay(delay); + } + animator.addListener(getGlobalAnimationFinishedListener()); + // remove the tag when the animation is finished + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + child.setTag(TAG_ANIMATOR_TOP_INSET, null); + child.setTag(TAG_START_TOP_INSET, null); + child.setTag(TAG_END_TOP_INSET, null); + } + }); + startAnimator(animator); + child.setTag(TAG_ANIMATOR_TOP_INSET, animator); + child.setTag(TAG_START_TOP_INSET, child.getClipTopAmount()); + child.setTag(TAG_END_TOP_INSET, newEndValue); + } + private void startAlphaAnimation(final ExpandableView child, final StackScrollState.ViewState viewState, long delay) { Float previousStartValue = getChildTag(child,TAG_START_ALPHA); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java index 25147b4..c2bd1cb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java @@ -17,12 +17,12 @@ package com.android.systemui.statusbar.tv; import android.os.IBinder; +import android.service.notification.NotificationListenerService.Ranking; import android.service.notification.StatusBarNotification; import android.view.View; import android.view.ViewGroup.LayoutParams; import android.view.WindowManager; -import com.android.internal.policy.IKeyguardShowCallback; import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.statusbar.BaseStatusBar; @@ -50,7 +50,7 @@ public class TvStatusBar extends BaseStatusBar { } @Override - public void addNotificationInternal(StatusBarNotification notification) { + public void addNotificationInternal(StatusBarNotification notification, Ranking ranking) { } @Override @@ -58,7 +58,7 @@ public class TvStatusBar extends BaseStatusBar { } @Override - protected void removeNotificationInternal(String key) { + protected void removeNotificationInternal(String key, Ranking ranking) { } @Override @@ -117,7 +117,7 @@ public class TvStatusBar extends BaseStatusBar { } @Override - protected void updateNotificationIcons() { + protected void updateNotifications() { } @Override |