diff options
Diffstat (limited to 'packages')
48 files changed, 1364 insertions, 406 deletions
diff --git a/packages/Keyguard/res/layout/keyguard_status_view.xml b/packages/Keyguard/res/layout/keyguard_status_view.xml index f79819f..112e371 100644 --- a/packages/Keyguard/res/layout/keyguard_status_view.xml +++ b/packages/Keyguard/res/layout/keyguard_status_view.xml @@ -39,13 +39,12 @@ android:id="@+id/clock_view" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="center_horizontal|top" + android:layout_gravity="center_horizontal" android:textColor="@color/clock_white" android:singleLine="true" style="@style/widget_big_thin" android:format12Hour="@string/keyguard_widget_12_hours_format" android:format24Hour="@string/keyguard_widget_24_hours_format" - android:baselineAligned="true" android:layout_marginBottom="@dimen/bottom_text_spacing_digital" /> <include layout="@layout/keyguard_status_area" /> diff --git a/packages/Keyguard/res/values-sw600dp-land/dimens.xml b/packages/Keyguard/res/values-sw600dp-land/dimens.xml index 5615ff7..13a6f62 100644 --- a/packages/Keyguard/res/values-sw600dp-land/dimens.xml +++ b/packages/Keyguard/res/values-sw600dp-land/dimens.xml @@ -26,5 +26,4 @@ <!-- Overload default clock widget parameters --> <dimen name="widget_big_font_size">88dp</dimen> - <dimen name="bottom_text_spacing_digital">-24dp</dimen> </resources>
\ No newline at end of file diff --git a/packages/Keyguard/res/values-sw600dp/dimens.xml b/packages/Keyguard/res/values-sw600dp/dimens.xml index a5e93dc..b954792 100644 --- a/packages/Keyguard/res/values-sw600dp/dimens.xml +++ b/packages/Keyguard/res/values-sw600dp/dimens.xml @@ -65,7 +65,7 @@ <!-- Overload default clock widget parameters --> <dimen name="widget_big_font_size">96dp</dimen> <dimen name="widget_label_font_size">16sp</dimen> - <dimen name="bottom_text_spacing_digital">-24dp</dimen> + <dimen name="bottom_text_spacing_digital">-8dp</dimen> <!-- EmergencyCarrierArea overlap - amount to overlap the emergency button and carrier text. Should be 0 on devices with plenty of room (e.g. tablets) --> diff --git a/packages/Keyguard/res/values/dimens.xml b/packages/Keyguard/res/values/dimens.xml index 6224aed..3830df7 100644 --- a/packages/Keyguard/res/values/dimens.xml +++ b/packages/Keyguard/res/values/dimens.xml @@ -155,7 +155,7 @@ <dimen name="eca_overlap">-10dip</dimen> <!-- Default clock parameters --> - <dimen name="bottom_text_spacing_digital">-18dp</dimen> + <dimen name="bottom_text_spacing_digital">-6dp</dimen> <dimen name="label_font_size">14dp</dimen> <dimen name="widget_label_font_size">14sp</dimen> <dimen name="widget_big_font_size">68dp</dimen> diff --git a/packages/Keyguard/res/values/strings.xml b/packages/Keyguard/res/values/strings.xml index d20b269..8cf07fa 100644 --- a/packages/Keyguard/res/values/strings.xml +++ b/packages/Keyguard/res/values/strings.xml @@ -97,9 +97,9 @@ <string name="keyguard_sim_unlock_progress_dialog_message">Unlocking SIM card\u2026</string> <!-- Time format strings for fall-back clock widget --> - <string name="keyguard_widget_12_hours_format" translatable="false">h:mm</string> + <string name="keyguard_widget_12_hours_format" translatable="false">h\uee01mm</string> <!-- Time format strings for fall-back clock widget --> - <string name="keyguard_widget_24_hours_format" translatable="false">kk:mm</string> + <string name="keyguard_widget_24_hours_format" translatable="false">kk\uee01mm</string> <!-- Accessibility description sent when user changes the current lock screen widget. [CHAR_LIMIT=none] --> <string name="keyguard_accessibility_widget_changed">%1$s. Widget %2$d of %3$d.</string> diff --git a/packages/Keyguard/res/values/styles.xml b/packages/Keyguard/res/values/styles.xml index 5ab00d2..11142cf 100644 --- a/packages/Keyguard/res/values/styles.xml +++ b/packages/Keyguard/res/values/styles.xml @@ -59,8 +59,6 @@ <!-- Built-in clock widget stuff --> <style name="widget_label"> - <item name="android:textStyle">bold</item> - <item name="android:fontFamily">sans-serif-light</item> <item name="android:textSize">@dimen/widget_label_font_size</item> </style> <style name="big_thin"> diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java index 2685447..d2bf30c 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java @@ -112,7 +112,9 @@ public class KeyguardHostView extends KeyguardViewBase { } public interface OnDismissAction { - /* returns true if the dismiss should be deferred */ + /** + * @return true if the dismiss should be deferred + */ boolean onDismiss(); } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java index ae55c4a..bef94fa 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java @@ -95,6 +95,10 @@ public class KeyguardStatusView extends GridLayout { final boolean screenOn = KeyguardUpdateMonitor.getInstance(mContext).isScreenOn(); setEnableMarquee(screenOn); refresh(); + + // Disable elegant text height because our fancy colon makes the ymin value huge for no + // reason. + mClockView.setElegantTextHeight(false); } protected void refresh() { @@ -164,6 +168,10 @@ public class KeyguardStatusView extends GridLayout { clockView24 = DateFormat.getBestDateTimePattern(locale, clockView24Skel); + // Use fancy colon. + clockView24 = clockView24.replace(':', '\uee01'); + clockView12 = clockView12.replace(':', '\uee01'); + cacheKey = key; } } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java index a9206e7..48b7be9 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java @@ -237,11 +237,6 @@ public abstract class KeyguardViewBase extends FrameLayout implements SecurityCa if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode())); mSecurityContainer.showPrimarySecurityScreen(false); mSecurityContainer.onResume(KeyguardSecurityView.SCREEN_ON); - - // This is a an attempt to fix bug 7137389 where the device comes back on but the entire - // layout is blank but forcing a layout causes it to reappear (e.g. with with - // hierarchyviewer). - requestLayout(); requestFocus(); } diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 6b62c25..e9cb197 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -52,6 +52,8 @@ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" /> + <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" /> + <!-- Physical hardware --> <uses-permission android:name="android.permission.MANAGE_USB" /> diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_default_user.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_default_user.png Binary files differdeleted file mode 100644 index 18257e0..0000000 --- a/packages/SystemUI/res/drawable-hdpi/ic_qs_default_user.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_default_user.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_default_user.png Binary files differdeleted file mode 100644 index a35c30d..0000000 --- a/packages/SystemUI/res/drawable-mdpi/ic_qs_default_user.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_default_user.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_default_user.png Binary files differdeleted file mode 100644 index d14a67f..0000000 --- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_default_user.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_default_user.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_default_user.png Binary files differdeleted file mode 100644 index 07f16c3..0000000 --- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_default_user.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable/ic_account_circle.xml b/packages/SystemUI/res/drawable/ic_account_circle.xml new file mode 100644 index 0000000..a7e8514 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_account_circle.xml @@ -0,0 +1,28 @@ +<!-- +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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="24dp" + android:height="24dp"/> + + <viewport + android:viewportWidth="24.0" + android:viewportHeight="24.0"/> + + <path + android:fill="#FFFFFFFF" + android:pathData="M12.0,2.0C6.5,2.0 2.0,6.5 2.0,12.0s4.5,10.0 10.0,10.0c5.5,0.0 10.0,-4.5 10.0,-10.0S17.5,2.0 12.0,2.0zM12.0,5.0c1.7,0.0 3.0,1.3 3.0,3.0c0.0,1.7 -1.3,3.0 -3.0,3.0c-1.7,0.0 -3.0,-1.3 -3.0,-3.0C9.0,6.3 10.3,5.0 12.0,5.0zM12.0,19.2c-2.5,0.0 -4.7,-1.3 -6.0,-3.2c0.0,-2.0 4.0,-3.1 6.0,-3.1c2.0,0.0 6.0,1.1 6.0,3.1C16.7,17.9 14.5,19.2 12.0,19.2z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/notification_header_bg.xml b/packages/SystemUI/res/drawable/notification_header_bg.xml index 09d0d7d..5daec20 100644 --- a/packages/SystemUI/res/drawable/notification_header_bg.xml +++ b/packages/SystemUI/res/drawable/notification_header_bg.xml @@ -19,13 +19,11 @@ <item android:state_pressed="true"> <shape> <solid android:color="@color/background_color_1_press" /> - <corners android:radius="@*android:dimen/notification_quantum_rounded_rect_radius" /> </shape> </item> <item> <shape> <solid android:color="@color/background_color_1" /> - <corners android:radius="@*android:dimen/notification_quantum_rounded_rect_radius" /> </shape> </item> </selector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/qs_panel_background.xml b/packages/SystemUI/res/drawable/qs_panel_background.xml index c324976..a1a5362 100644 --- a/packages/SystemUI/res/drawable/qs_panel_background.xml +++ b/packages/SystemUI/res/drawable/qs_panel_background.xml @@ -13,11 +13,8 @@ See the License for the specific language governing permissions and limitations under the License. --> -<inset xmlns:android="http://schemas.android.com/apk/res/android" - android:insetLeft="@dimen/notification_side_padding" - android:insetRight="@dimen/notification_side_padding"> - <shape> - <solid android:color="@color/system_primary_color" /> - <corners android:radius="@*android:dimen/notification_quantum_rounded_rect_radius" /> - </shape> -</inset> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <solid android:color="@color/system_primary_color" /> + <corners + android:radius="@*android:dimen/notification_quantum_rounded_rect_radius"/> +</shape> diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml index 2ec3766..21e5390 100644 --- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml +++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml @@ -53,7 +53,8 @@ android:layout_marginBottom="100dp" android:layout_gravity="bottom|center_horizontal" android:textStyle="italic" - android:textAppearance="?android:attr/textAppearanceMedium"/> + android:textColor="#ffffff" + android:textAppearance="?android:attr/textAppearanceSmall"/> <ImageView android:id="@+id/lock_icon" diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml index 85de645..398787f 100644 --- a/packages/SystemUI/res/layout/qs_panel.xml +++ b/packages/SystemUI/res/layout/qs_panel.xml @@ -16,11 +16,10 @@ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/quick_settings_container" - android:paddingLeft="@dimen/notification_side_padding" - android:paddingRight="@dimen/notification_side_padding" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@drawable/qs_panel_background" > + android:background="@drawable/qs_panel_background" + android:elevation="2dp"> <com.android.systemui.qs.QSPanel android:id="@+id/quick_settings_panel" android:background="#0000" diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml index 2ec9935..cde83bf 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded.xml @@ -59,8 +59,8 @@ android:id="@+id/scroll_view" android:layout_width="match_parent" android:layout_height="match_parent" - android:visibility="invisible" android:scrollbars="none" + android:overScrollMode="never" android:fillViewport="true"> <LinearLayout android:layout_width="match_parent" @@ -70,7 +70,9 @@ layout="@layout/qs_panel" android:layout_marginTop="@dimen/status_bar_header_height_expanded" android:layout_width="match_parent" - android:layout_height="wrap_content"/> + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/notification_side_padding" + android:layout_marginRight="@dimen/notification_side_padding"/> <!-- A view to reserve space for the collapsed stack --> <View @@ -79,7 +81,6 @@ </LinearLayout> </com.android.systemui.statusbar.phone.ObservableScrollView> - <com.android.systemui.statusbar.stack.NotificationStackScrollLayout android:id="@+id/notification_stack_scroller" android:layout_width="match_parent" diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml index 89fa988..dfc3b22 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml @@ -25,7 +25,7 @@ android:paddingStart="@dimen/notification_side_padding" android:paddingEnd="@dimen/notification_side_padding" android:baselineAligned="false" - android:elevation="10dp" + android:elevation="4dp" > <View @@ -65,22 +65,13 @@ /> </RelativeLayout> - <com.android.keyguard.CarrierText - android:id="@+id/keyguard_carrier_text" - android:layout_width="wrap_content" - android:layout_height="@dimen/status_bar_header_height_keyguard" - android:layout_marginLeft="8dp" - android:gravity="center_vertical" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium" /> - <com.android.systemui.statusbar.phone.MultiUserSwitch android:id="@+id/multi_user_switch" android:layout_width="40dp" android:layout_height="@dimen/status_bar_header_height" android:layout_alignParentEnd="true" android:background="@null" android:scaleType="centerInside" - android:padding="6dp" + android:padding="8dp" /> <ImageButton android:id="@+id/settings_button" @@ -98,6 +89,17 @@ android:layout_marginEnd="4dp" /> + <com.android.keyguard.CarrierText + android:id="@+id/keyguard_carrier_text" + android:layout_width="match_parent" + android:layout_height="@dimen/status_bar_header_height_keyguard" + android:layout_marginLeft="8dp" + android:layout_toStartOf="@id/system_icons_container" + android:gravity="center_vertical" + android:ellipsize="marquee" + android:textAppearance="?android:attr/textAppearanceSmall" + android:textColor="#ffffff" /> + <include layout="@layout/quick_settings_brightness_dialog" android:id="@+id/brightness_container" diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml index 22815f3..5750faa 100644 --- a/packages/SystemUI/res/values-sw600dp/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp/dimens.xml @@ -57,6 +57,6 @@ <!-- The margin between the clock and the notifications on Keyguard. See keyguard_clock_height_fraction_* for the difference between min and max.--> - <dimen name="keyguard_clock_notifications_margin_min">32dp</dimen> - <dimen name="keyguard_clock_notifications_margin_max">32dp</dimen> + <dimen name="keyguard_clock_notifications_margin_min">36dp</dimen> + <dimen name="keyguard_clock_notifications_margin_max">36dp</dimen> </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index c209434..bf0cb68 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -196,6 +196,9 @@ <dimen name="qs_dual_tile_height">109dp</dimen> <dimen name="qs_dual_tile_padding">12dp</dimen> + <!-- How far the expanded QS panel peeks from the header in collapsed state. --> + <dimen name="qs_peek_height">8dp</dimen> + <!-- used by DessertCase --> <dimen name="dessert_case_cell_size">192dp</dimen> @@ -246,7 +249,7 @@ <dimen name="notification_side_padding">8dp</dimen> <!-- Z distance between notifications if they are in the stack --> - <dimen name="z_distance_between_notifications">2dp</dimen> + <dimen name="z_distance_between_notifications">1dp</dimen> <!-- The padding between the individual notification cards when dimmed. --> <dimen name="notification_padding_dimmed">0dp</dimen> diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java index c0f9bf2..191bac9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java @@ -20,6 +20,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.net.ConnectivityManager; import android.provider.Settings.Global; import com.android.systemui.R; @@ -52,10 +53,9 @@ public class AirplaneModeTile extends QSTile<QSTile.BooleanState> { } private void setEnabled(boolean enabled) { - mSetting.setValue(enabled ? 1 : 0); - final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); - intent.putExtra("state", enabled); - mContext.sendBroadcast(intent); + final ConnectivityManager mgr = + (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); + mgr.setAirplaneMode(enabled); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java index ac16164..e3dac4a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java @@ -21,15 +21,25 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.RectF; +import android.graphics.Shader; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; +import android.view.animation.LinearInterpolator; import android.view.animation.PathInterpolator; - import com.android.systemui.R; +import com.android.systemui.statusbar.stack.StackStateAnimator; /** * Base class for both {@link ExpandableNotificationRow} and {@link NotificationOverflowContainer} @@ -41,6 +51,36 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView private static final int BACKGROUND_ANIMATION_LENGTH_MS = 220; private static final int ACTIVATE_ANIMATION_LENGTH = 220; + /** + * The amount of width, which is kept in the end when performing a disappear animation (also + * the amount from which the horizontal appearing begins) + */ + private static final float HORIZONTAL_COLLAPSED_REST_PARTIAL = 0.05f; + + /** + * At which point from [0,1] does the horizontal collapse animation end (or start when + * expanding)? 1.0 meaning that it ends immediately and 0.0 that it is continuously animated. + */ + private static final float HORIZONTAL_ANIMATION_END = 0.2f; + + /** + * At which point from [0,1] does the alpha animation end (or start when + * expanding)? 1.0 meaning that it ends immediately and 0.0 that it is continuously animated. + */ + private static final float ALPHA_ANIMATION_END = 0.0f; + + /** + * At which point from [0,1] does the horizontal collapse animation start (or start when + * expanding)? 1.0 meaning that it starts immediately and 0.0 that it is animated at all. + */ + private static final float HORIZONTAL_ANIMATION_START = 1.0f; + + /** + * At which point from [0,1] does the vertical collapse animation start (or end when + * expanding) 1.0 meaning that it starts immediately and 0.0 that it is animated at all. + */ + private static final float VERTICAL_ANIMATION_START = 1.0f; + private static final Interpolator ACTIVATE_INVERSE_INTERPOLATOR = new PathInterpolator(0.6f, 0, 0.5f, 1); private static final Interpolator ACTIVATE_INVERSE_ALPHA_INTERPOLATOR @@ -53,6 +93,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView private int mBgTint = 0; private int mDimmedBgTint = 0; + private final int mRoundedRectCornerRadius; /** * Flag to indicate that the notification has been touched once and the second touch will @@ -66,22 +107,41 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView private OnActivatedListener mOnActivatedListener; - private Interpolator mLinearOutSlowInInterpolator; - private Interpolator mFastOutSlowInInterpolator; + private final Interpolator mLinearOutSlowInInterpolator; + private final Interpolator mFastOutSlowInInterpolator; + private final Interpolator mSlowOutFastInInterpolator; + private final Interpolator mSlowOutLinearInInterpolator; + private final Interpolator mLinearInterpolator; + private Interpolator mCurrentAppearInterpolator; + private Interpolator mCurrentAlphaInterpolator; private NotificationBackgroundView mBackgroundNormal; private NotificationBackgroundView mBackgroundDimmed; private ObjectAnimator mBackgroundAnimator; + private RectF mAppearAnimationRect = new RectF(); + private PorterDuffColorFilter mAppearAnimationFilter; + private float mAnimationTranslationY; + private boolean mDrawingAppearAnimation; + private Paint mAppearPaint = new Paint(); + private ValueAnimator mAppearAnimator; + private float mAppearAnimationFraction = -1.0f; + private float mAppearAnimationTranslation; public ActivatableNotificationView(Context context, AttributeSet attrs) { super(context, attrs); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in); + mSlowOutFastInInterpolator = new PathInterpolator(0.8f, 0.0f, 0.6f, 1.0f); mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, android.R.interpolator.linear_out_slow_in); + mSlowOutLinearInInterpolator = new PathInterpolator(0.8f, 0.0f, 1.0f, 1.0f); + mLinearInterpolator = new LinearInterpolator(); setClipChildren(false); setClipToPadding(false); + mAppearAnimationFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_ATOP); + mRoundedRectCornerRadius = getResources().getDimensionPixelSize( + com.android.internal.R.dimen.notification_quantum_rounded_rect_radius); } @Override @@ -316,6 +376,202 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView mBackgroundDimmed.setClipTopAmount(clipTopAmount); } + @Override + public void performRemoveAnimation(float translationDirection, Runnable onFinishedRunnable) { + enableAppearDrawing(true); + if (mDrawingAppearAnimation) { + startAppearAnimation(false /* isAppearing */, translationDirection, + 0, onFinishedRunnable); + } + } + + @Override + public void performAddAnimation(long delay) { + enableAppearDrawing(true); + if (mDrawingAppearAnimation) { + startAppearAnimation(true /* isAppearing */, -1.0f, delay, null); + } + } + + private void startAppearAnimation(boolean isAppearing, + float translationDirection, long delay, final Runnable onFinishedRunnable) { + if (mAppearAnimator != null) { + mAppearAnimator.cancel(); + } + mAnimationTranslationY = translationDirection * mActualHeight; + if (mAppearAnimationFraction == -1.0f) { + // not initialized yet, we start anew + if (isAppearing) { + mAppearAnimationFraction = 0.0f; + mAppearAnimationTranslation = mAnimationTranslationY; + } else { + mAppearAnimationFraction = 1.0f; + mAppearAnimationTranslation = 0; + } + } + + float targetValue; + if (isAppearing) { + mCurrentAppearInterpolator = mSlowOutFastInInterpolator; + mCurrentAlphaInterpolator = mLinearOutSlowInInterpolator; + targetValue = 1.0f; + } else { + mCurrentAppearInterpolator = mFastOutSlowInInterpolator; + mCurrentAlphaInterpolator = mSlowOutLinearInInterpolator; + targetValue = 0.0f; + } + mAppearAnimator = ValueAnimator.ofFloat(mAppearAnimationFraction, + targetValue); + mAppearAnimator.setInterpolator(mLinearInterpolator); + mAppearAnimator.setDuration( + (long) (StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR + * Math.abs(mAppearAnimationFraction - targetValue))); + mAppearAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + mAppearAnimationFraction = (float) animation.getAnimatedValue(); + updateAppearAnimationAlpha(); + updateAppearRect(); + invalidate(); + } + }); + if (delay > 0) { + // we need to apply the initial state already to avoid drawn frames in the wrong state + updateAppearAnimationAlpha(); + updateAppearRect(); + mAppearAnimator.setStartDelay(delay); + } + mAppearAnimator.addListener(new AnimatorListenerAdapter() { + private boolean mWasCancelled; + + @Override + public void onAnimationEnd(Animator animation) { + if (onFinishedRunnable != null) { + onFinishedRunnable.run(); + } + if (!mWasCancelled) { + mAppearAnimationFraction = -1; + setOutlineRect(null); + enableAppearDrawing(false); + } + } + + @Override + public void onAnimationStart(Animator animation) { + mWasCancelled = false; + } + + @Override + public void onAnimationCancel(Animator animation) { + mWasCancelled = true; + } + }); + mAppearAnimator.start(); + } + + private void updateAppearRect() { + float inverseFraction = (1.0f - mAppearAnimationFraction); + float translationFraction = mCurrentAppearInterpolator.getInterpolation(inverseFraction); + float translateYTotalAmount = translationFraction * mAnimationTranslationY; + mAppearAnimationTranslation = translateYTotalAmount; + + // handle width animation + float widthFraction = (inverseFraction - (1.0f - HORIZONTAL_ANIMATION_START)) + / (HORIZONTAL_ANIMATION_START - HORIZONTAL_ANIMATION_END); + widthFraction = Math.min(1.0f, Math.max(0.0f, widthFraction)); + widthFraction = mCurrentAppearInterpolator.getInterpolation(widthFraction); + float left = (getWidth() * (0.5f - HORIZONTAL_COLLAPSED_REST_PARTIAL / 2.0f) * + widthFraction); + float right = getWidth() - left; + + // handle top animation + float heightFraction = (inverseFraction - (1.0f - VERTICAL_ANIMATION_START)) / + VERTICAL_ANIMATION_START; + heightFraction = Math.max(0.0f, heightFraction); + heightFraction = mCurrentAppearInterpolator.getInterpolation(heightFraction); + + float top; + float bottom; + if (mAnimationTranslationY > 0.0f) { + bottom = mActualHeight - heightFraction * mAnimationTranslationY * 0.1f + - translateYTotalAmount; + top = bottom * heightFraction; + } else { + top = heightFraction * (mActualHeight + mAnimationTranslationY) * 0.1f - + translateYTotalAmount; + bottom = mActualHeight * (1 - heightFraction) + top * heightFraction; + } + mAppearAnimationRect.set(left, top, right, bottom); + setOutlineRect(left, top + mAppearAnimationTranslation, right, + bottom + mAppearAnimationTranslation); + } + + private void updateAppearAnimationAlpha() { + int backgroundColor = getBackgroundColor(); + if (backgroundColor != -1) { + float contentAlphaProgress = mAppearAnimationFraction; + contentAlphaProgress = contentAlphaProgress / (1.0f - ALPHA_ANIMATION_END); + contentAlphaProgress = Math.min(1.0f, contentAlphaProgress); + contentAlphaProgress = mCurrentAlphaInterpolator.getInterpolation(contentAlphaProgress); + int sourceColor = Color.argb((int) (255 * (1.0f - contentAlphaProgress)), + Color.red(backgroundColor), Color.green(backgroundColor), + Color.blue(backgroundColor)); + mAppearAnimationFilter.setColor(sourceColor); + mAppearPaint.setColorFilter(mAppearAnimationFilter); + } + } + + private int getBackgroundColor() { + // TODO: get real color + return 0xfffafafa; + } + + /** + * When we draw the appear animation, we render the view in a bitmap and render this bitmap + * as a shader of a rect. This call creates the Bitmap and switches the drawing mode, + * such that the normal drawing of the views does not happen anymore. + * + * @param enable Should it be enabled. + */ + private void enableAppearDrawing(boolean enable) { + if (enable != mDrawingAppearAnimation) { + if (enable) { + if (getWidth() == 0 || getActualHeight() == 0) { + // TODO: This should not happen, but it can during expansion. Needs + // investigation + return; + } + Bitmap bitmap = Bitmap.createBitmap(getWidth(), getActualHeight(), + Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + draw(canvas); + mAppearPaint.setShader(new BitmapShader(bitmap, Shader.TileMode.CLAMP, + Shader.TileMode.CLAMP)); + } else { + mAppearPaint.setShader(null); + } + mDrawingAppearAnimation = enable; + invalidate(); + } + } + + @Override + protected void dispatchDraw(Canvas canvas) { + if (!mDrawingAppearAnimation) { + super.dispatchDraw(canvas); + } else { + drawAppearRect(canvas); + } + } + + private void drawAppearRect(Canvas canvas) { + canvas.save(); + canvas.translate(0, mAppearAnimationTranslation); + canvas.drawRoundRect(mAppearAnimationRect, mRoundedRectCornerRadius, + mRoundedRectCornerRadius, mAppearPaint); + canvas.restore(); + } + public void setOnActivatedListener(OnActivatedListener onActivatedListener) { mOnActivatedListener = onActivatedListener; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 457d32e..f4db625 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -22,6 +22,7 @@ import android.app.Notification; import android.app.PendingIntent; import android.app.TaskStackBuilder; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -46,6 +47,7 @@ import android.os.UserManager; import android.provider.Settings; import android.service.dreams.DreamService; import android.service.dreams.IDreamManager; +import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.text.TextUtils; import android.util.Log; @@ -79,13 +81,17 @@ 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.Locale; +import static com.android.keyguard.KeyguardHostView.OnDismissAction; + public abstract class BaseStatusBar extends SystemUI implements CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener { 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; protected static final int MSG_SHOW_RECENT_APPS = 1019; protected static final int MSG_HIDE_RECENT_APPS = 1020; @@ -158,6 +164,7 @@ public abstract class BaseStatusBar extends SystemUI implements protected WindowManager mWindowManager; protected IWindowManager mWindowManagerService; + protected abstract void refreshLayout(int layoutDirection); protected Display mDisplay; @@ -208,33 +215,47 @@ public abstract class BaseStatusBar extends SystemUI implements private RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() { @Override - public boolean onClickHandler(View view, PendingIntent pendingIntent, Intent fillInIntent) { + public boolean onClickHandler( + final View view, final PendingIntent pendingIntent, final Intent fillInIntent) { if (DEBUG) { Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent); } final boolean isActivity = pendingIntent.isActivity(); if (isActivity) { - try { - // The intent we are sending is for the application, which - // won't have permission to immediately start an activity after - // the user switches to home. We know it is safe to do at this - // point, so make sure new activity switches are now allowed. - ActivityManagerNative.getDefault().resumeAppSwitches(); - // Also, notifications can be launched from the lock screen, - // so dismiss the lock screen when the activity starts. - ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity(); - } catch (RemoteException e) { - } - } + startNotificationActivity(new OnDismissAction() { + @Override + public boolean onDismiss() { + try { + // The intent we are sending is for the application, which + // won't have permission to immediately start an activity after + // the user switches to home. We know it is safe to do at this + // point, so make sure new activity switches are now allowed. + ActivityManagerNative.getDefault().resumeAppSwitches(); + // Also, notifications can be launched from the lock screen, + // so dismiss the lock screen when the activity starts. + ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity(); + } catch (RemoteException e) { + } - boolean handled = super.onClickHandler(view, pendingIntent, fillInIntent); + boolean handled = superOnClickHandler(view, pendingIntent, fillInIntent); - if (isActivity && handled) { - // close the shade if it was open - animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); - visibilityChanged(false); + // close the shade if it was open + if (handled) { + animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); + visibilityChanged(false); + } + return handled; // Wait for activity start. + } + }); + return true; + } else { + return super.onClickHandler(view, pendingIntent, fillInIntent); } - return handled; + } + + private boolean superOnClickHandler(View view, PendingIntent pendingIntent, + Intent fillInIntent) { + return super.onClickHandler(view, pendingIntent, fillInIntent); } }; @@ -253,6 +274,49 @@ public abstract class BaseStatusBar extends SystemUI implements } }; + private final NotificationListenerService mNotificationListener = + new NotificationListenerService() { + @Override + public void onListenerConnected() { + if (DEBUG) Log.d(TAG, "onListenerConnected"); + final StatusBarNotification[] notifications = getActiveNotifications(); + mHandler.post(new Runnable() { + @Override + public void run() { + for (StatusBarNotification sbn : notifications) { + addNotificationInternal(sbn); + } + } + }); + } + + @Override + public void onNotificationPosted(final StatusBarNotification sbn) { + if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn); + mHandler.post(new Runnable() { + @Override + public void run() { + if (mNotificationData.findByKey(sbn.getKey()) != null) { + updateNotificationInternal(sbn); + } else { + addNotificationInternal(sbn); + } + } + }); + } + + @Override + public void onNotificationRemoved(final StatusBarNotification sbn) { + if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn); + mHandler.post(new Runnable() { + @Override + public void run() { + removeNotificationInternal(sbn.getKey()); + } + }); + } + }; + private void updateCurrentProfilesCache() { synchronized (mCurrentProfiles) { mCurrentProfiles.clear(); @@ -299,14 +363,13 @@ public abstract class BaseStatusBar extends SystemUI implements // Connect in to the status bar manager service StatusBarIconList iconList = new StatusBarIconList(); - ArrayList<IBinder> notificationKeys = new ArrayList<IBinder>(); ArrayList<StatusBarNotification> notifications = new ArrayList<StatusBarNotification>(); mCommandQueue = new CommandQueue(this, iconList); int[] switches = new int[8]; ArrayList<IBinder> binders = new ArrayList<IBinder>(); try { - mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys, notifications, + mBarService.registerStatusBar(mCommandQueue, iconList, notifications, switches, binders); } catch (RemoteException ex) { // If the system process isn't there we're doomed anyway. @@ -332,17 +395,23 @@ public abstract class BaseStatusBar extends SystemUI implements } } - // Set up the initial notification state - N = notificationKeys.size(); - if (N == notifications.size()) { - for (int i=0; i<N; i++) { - addNotification(notificationKeys.get(i), notifications.get(i)); + // Set up the initial notification state. + if (USE_NOTIFICATION_LISTENER) { + try { + mNotificationListener.registerAsSystemService( + new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()), + UserHandle.USER_ALL); + } catch (RemoteException e) { + Log.e(TAG, "Unable to register notification listener", e); } } else { - Log.wtf(TAG, "Notification list length mismatch: keys=" + N - + " notifications=" + notifications.size()); + N = notifications.size(); + for (int i=0; i<N; i++) { + addNotification(notifications.get(i)); + } } + if (DEBUG) { Log.d(TAG, String.format( "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x", @@ -381,6 +450,14 @@ public abstract class BaseStatusBar extends SystemUI implements } } + /** + * Takes the necessary steps to prepare the status bar for starting an activity, then starts it. + * @param action A dismiss action that is called if it's safe to start the activity. + */ + protected void startNotificationActivity(OnDismissAction action) { + action.onDismiss(); + } + @Override protected void onConfigurationChanged(Configuration newConfig) { final Locale locale = mContext.getResources().getConfiguration().locale; @@ -946,47 +1023,55 @@ public abstract class BaseStatusBar extends SystemUI implements mIsHeadsUp = forHun; } - public void onClick(View v) { - try { - // The intent we are sending is for the application, which - // won't have permission to immediately start an activity after - // the user switches to home. We know it is safe to do at this - // point, so make sure new activity switches are now allowed. - ActivityManagerNative.getDefault().resumeAppSwitches(); - // Also, notifications can be launched from the lock screen, - // so dismiss the lock screen when the activity starts. - ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity(); - } catch (RemoteException e) { - } + public void onClick(final View v) { + startNotificationActivity(new OnDismissAction() { + public boolean onDismiss() { + try { + // The intent we are sending is for the application, which + // won't have permission to immediately start an activity after + // the user switches to home. We know it is safe to do at this + // point, so make sure new activity switches are now allowed. + ActivityManagerNative.getDefault().resumeAppSwitches(); + // Also, notifications can be launched from the lock screen, + // so dismiss the lock screen when the activity starts. + ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity(); + } catch (RemoteException e) { + } - if (mIntent != null) { - int[] pos = new int[2]; - v.getLocationOnScreen(pos); - Intent overlay = new Intent(); - overlay.setSourceBounds( - new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight())); - try { - mIntent.send(mContext, 0, overlay); - } catch (PendingIntent.CanceledException e) { - // the stack trace isn't very helpful here. Just log the exception message. - Log.w(TAG, "Sending contentIntent failed: " + e); - } + boolean sent = false; + if (mIntent != null) { + int[] pos = new int[2]; + v.getLocationOnScreen(pos); + Intent overlay = new Intent(); + overlay.setSourceBounds(new Rect(pos[0], pos[1], + pos[0]+v.getWidth(), pos[1]+v.getHeight())); + try { + mIntent.send(mContext, 0, overlay); + sent = true; + } catch (PendingIntent.CanceledException e) { + // the stack trace isn't very helpful here. + // Just log the exception message. + Log.w(TAG, "Sending contentIntent failed: " + e); + } + } - KeyguardTouchDelegate.getInstance(mContext).dismiss(); - } + try { + if (mIsHeadsUp) { + mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP); + } + mBarService.onNotificationClick(mNotificationKey); + } catch (RemoteException ex) { + // system process is dead if we're here. + } - try { - if (mIsHeadsUp) { - mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP); - } - mBarService.onNotificationClick(mNotificationKey); - } catch (RemoteException ex) { - // system process is dead if we're here. - } + // close the shade if it was open + animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); + visibilityChanged(false); - // close the shade if it was open - animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); - visibilityChanged(false); + boolean waitForActivityLaunch = sent && mIntent.isActivity(); + return waitForActivityLaunch; + } + }); } } @@ -1018,8 +1103,8 @@ public abstract class BaseStatusBar extends SystemUI implements * * WARNING: this will call back into us. Don't hold any locks. */ - void handleNotificationError(IBinder key, StatusBarNotification n, String message) { - removeNotification(key); + void handleNotificationError(StatusBarNotification n, String message) { + removeNotification(n.getKey()); try { mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(), n.getInitialPid(), message, n.getUserId()); @@ -1028,7 +1113,7 @@ public abstract class BaseStatusBar extends SystemUI implements } } - protected StatusBarNotification removeNotificationViews(IBinder key) { + protected StatusBarNotification removeNotificationViews(String key) { NotificationData.Entry entry = mNotificationData.remove(key); if (entry == null) { Log.w(TAG, "removeNotification for unknown key: " + key); @@ -1039,14 +1124,14 @@ public abstract class BaseStatusBar extends SystemUI implements if (rowParent != null) rowParent.removeView(entry.row); updateRowStates(); updateNotificationIcons(); + updateSpeedBump(); return entry.notification; } - protected NotificationData.Entry createNotificationViews(IBinder key, - StatusBarNotification notification) { + protected NotificationData.Entry createNotificationViews(StatusBarNotification notification) { if (DEBUG) { - Log.d(TAG, "createNotificationViews(key=" + key + ", notification=" + notification); + Log.d(TAG, "createNotificationViews(notification=" + notification); } // Construct the icon. final StatusBarIconView iconView = new StatusBarIconView(mContext, @@ -1061,13 +1146,13 @@ public abstract class BaseStatusBar extends SystemUI implements notification.getNotification().number, notification.getNotification().tickerText); if (!iconView.set(ic)) { - handleNotificationError(key, notification, "Couldn't create icon: " + ic); + handleNotificationError(notification, "Couldn't create icon: " + ic); return null; } // Construct the expanded view. - NotificationData.Entry entry = new NotificationData.Entry(key, notification, iconView); + NotificationData.Entry entry = new NotificationData.Entry(notification, iconView); if (!inflateViews(entry, mStackScroller)) { - handleNotificationError(key, notification, "Couldn't expand RemoteViews for: " + handleNotificationError(notification, "Couldn't expand RemoteViews for: " + notification); return null; } @@ -1083,12 +1168,26 @@ public abstract class BaseStatusBar extends SystemUI implements if (DEBUG) { Log.d(TAG, "addNotificationViews: added at " + pos); } - updateNotificationIcons(); 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); } - private void addNotificationViews(IBinder key, StatusBarNotification notification) { - addNotificationViews(createNotificationViews(key, notification)); + private void addNotificationViews(StatusBarNotification notification) { + addNotificationViews(createNotificationViews(notification)); } /** @@ -1104,7 +1203,6 @@ public abstract class BaseStatusBar extends SystemUI implements mKeyguardIconOverflowContainer.getIconsView().removeAllViews(); int n = mNotificationData.size(); int visibleNotifications = 0; - int speedBumpIndex = -1; boolean onKeyguard = mState == StatusBarState.KEYGUARD; for (int i = n-1; i >= 0; i--) { NotificationData.Entry entry = mNotificationData.get(i); @@ -1125,17 +1223,14 @@ public abstract class BaseStatusBar extends SystemUI implements mKeyguardIconOverflowContainer.getIconsView().addNotification(entry); } } else { - if (entry.row.getVisibility() == View.GONE) { + boolean wasGone = entry.row.getVisibility() == View.GONE; + entry.row.setVisibility(View.VISIBLE); + if (wasGone) { // notify the scroller of a child addition mStackScroller.generateAddAnimation(entry.row); } - entry.row.setVisibility(View.VISIBLE); visibleNotifications++; } - if (entry.row.getVisibility() != View.GONE && speedBumpIndex == -1 - && entry.row.isBelowSpeedBump() ) { - speedBumpIndex = n - 1 - i; - } } if (onKeyguard && mKeyguardIconOverflowContainer.getIconsView().getChildCount() > 0) { @@ -1143,8 +1238,6 @@ public abstract class BaseStatusBar extends SystemUI implements } else { mKeyguardIconOverflowContainer.setVisibility(View.GONE); } - - mStackScroller.updateSpeedBumpIndex(speedBumpIndex); } private boolean shouldShowOnKeyguard(StatusBarNotification sbn) { @@ -1160,7 +1253,7 @@ public abstract class BaseStatusBar extends SystemUI implements protected abstract void haltTicker(); protected abstract void setAreThereNotifications(); protected abstract void updateNotificationIcons(); - protected abstract void tick(IBinder key, StatusBarNotification n, boolean firstTime); + protected abstract void tick(StatusBarNotification n, boolean firstTime); protected abstract void updateExpandedViewPos(int expandedPosition); protected abstract boolean shouldDisableNavbarGestures(); @@ -1168,12 +1261,37 @@ public abstract class BaseStatusBar extends SystemUI implements return parent != null && parent.indexOfChild(entry.row) == 0; } - public void updateNotification(IBinder key, StatusBarNotification notification) { - if (DEBUG) Log.d(TAG, "updateNotification(" + key + " -> " + notification + ")"); - final NotificationData.Entry oldEntry = mNotificationData.findByKey(key); + @Override + public void addNotification(StatusBarNotification notification) { + if (!USE_NOTIFICATION_LISTENER) { + addNotificationInternal(notification); + } + } + + public abstract void addNotificationInternal(StatusBarNotification notification); + + @Override + public void removeNotification(String key) { + if (!USE_NOTIFICATION_LISTENER) { + removeNotificationInternal(key); + } + } + + protected abstract void removeNotificationInternal(String key); + + public void updateNotification(StatusBarNotification notification) { + if (!USE_NOTIFICATION_LISTENER) { + updateNotificationInternal(notification); + } + } + + public void updateNotificationInternal(StatusBarNotification notification) { + if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")"); + + final NotificationData.Entry oldEntry = mNotificationData.findByKey(notification.getKey()); if (oldEntry == null) { - Log.w(TAG, "updateNotification for unknown key: " + key); + Log.w(TAG, "updateNotification for unknown key: " + notification.getKey()); return; } @@ -1244,15 +1362,15 @@ public abstract class BaseStatusBar extends SystemUI implements boolean orderUnchanged = notification.getNotification().when == oldNotification.getNotification().when && notification.getScore() == oldNotification.getScore(); - // score now encompasses/supersedes isOngoing() + // score now encompasses/supersedes isOngoing() boolean updateTicker = notification.getNotification().tickerText != null && !TextUtils.equals(notification.getNotification().tickerText, - oldEntry.notification.getNotification().tickerText); + oldEntry.notification.getNotification().tickerText); boolean isTopAnyway = isTopNotification(rowParent, oldEntry); if (contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged && publicUnchanged && (orderUnchanged || isTopAnyway)) { - if (DEBUG) Log.d(TAG, "reusing notification for key: " + key); + if (DEBUG) Log.d(TAG, "reusing notification for key: " + notification.getKey()); oldEntry.notification = notification; try { updateNotificationViews(oldEntry, notification); @@ -1276,25 +1394,27 @@ public abstract class BaseStatusBar extends SystemUI implements notification.getNotification().number, notification.getNotification().tickerText); if (!oldEntry.icon.set(ic)) { - handleNotificationError(key, notification, "Couldn't update icon: " + ic); + handleNotificationError(notification, "Couldn't update icon: " + ic); return; } updateRowStates(); + updateSpeedBump(); } 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(key); - addNotificationViews(key, notification); + removeNotificationViews(notification.getKey()); + addNotificationViews(notification); } } else { - if (DEBUG) Log.d(TAG, "not reusing notification for key: " + key); + 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(key); - addNotificationViews(key, notification); // will also replace the heads up - final NotificationData.Entry newEntry = mNotificationData.findByKey(key); + removeNotificationViews(notification.getKey()); + addNotificationViews(notification); // will also replace the heads up + final NotificationData.Entry newEntry = mNotificationData.findByKey( + notification.getKey()); final boolean userChangedExpansion = oldEntry.row.hasUserChangedExpansion(); if (userChangedExpansion) { boolean userExpanded = oldEntry.row.isUserExpanded(); @@ -1314,7 +1434,7 @@ public abstract class BaseStatusBar extends SystemUI implements // Restart the ticker if it's still running if (updateTicker && isForCurrentUser) { haltTicker(); - tick(key, notification, false); + tick(notification, false); } // Recalculate the position of the sliding windows and the titles. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index b4a347b..aaeadb6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -21,7 +21,6 @@ import android.os.IBinder; import android.os.Message; import android.service.notification.StatusBarNotification; -import com.android.internal.policy.IKeyguardShowCallback; import com.android.internal.statusbar.IStatusBar; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.statusbar.StatusBarIconList; @@ -73,11 +72,6 @@ public class CommandQueue extends IStatusBar.Stub { private Callbacks mCallbacks; private Handler mHandler = new H(); - private class NotificationQueueEntry { - IBinder key; - StatusBarNotification notification; - } - /** * These methods are called back on the main thread. */ @@ -86,9 +80,9 @@ public class CommandQueue extends IStatusBar.Stub { public void updateIcon(String slot, int index, int viewIndex, StatusBarIcon old, StatusBarIcon icon); public void removeIcon(String slot, int index, int viewIndex); - public void addNotification(IBinder key, StatusBarNotification notification); - public void updateNotification(IBinder key, StatusBarNotification notification); - public void removeNotification(IBinder key); + public void addNotification(StatusBarNotification notification); + public void updateNotification(StatusBarNotification notification); + public void removeNotification(String key); public void disable(int state); public void animateExpandNotificationsPanel(); public void animateCollapsePanels(int flags); @@ -106,7 +100,6 @@ public class CommandQueue extends IStatusBar.Stub { public void showSearchPanel(); public void hideSearchPanel(); public void setWindowState(int window, int state); - } public CommandQueue(Callbacks callbacks, StatusBarIconList list) { @@ -130,25 +123,21 @@ public class CommandQueue extends IStatusBar.Stub { } } - public void addNotification(IBinder key, StatusBarNotification notification) { + @Override + public void addNotification(StatusBarNotification notification) { synchronized (mList) { - NotificationQueueEntry ne = new NotificationQueueEntry(); - ne.key = key; - ne.notification = notification; - mHandler.obtainMessage(MSG_ADD_NOTIFICATION, 0, 0, ne).sendToTarget(); + mHandler.obtainMessage(MSG_ADD_NOTIFICATION, 0, 0, notification).sendToTarget(); } } - public void updateNotification(IBinder key, StatusBarNotification notification) { + @Override + public void updateNotification(StatusBarNotification notification) { synchronized (mList) { - NotificationQueueEntry ne = new NotificationQueueEntry(); - ne.key = key; - ne.notification = notification; - mHandler.obtainMessage(MSG_UPDATE_NOTIFICATION, 0, 0, ne).sendToTarget(); + mHandler.obtainMessage(MSG_UPDATE_NOTIFICATION, 0, 0, notification).sendToTarget(); } } - public void removeNotification(IBinder key) { + public void removeNotification(String key) { synchronized (mList) { mHandler.obtainMessage(MSG_REMOVE_NOTIFICATION, 0, 0, key).sendToTarget(); } @@ -291,17 +280,15 @@ public class CommandQueue extends IStatusBar.Stub { break; } case MSG_ADD_NOTIFICATION: { - final NotificationQueueEntry ne = (NotificationQueueEntry)msg.obj; - mCallbacks.addNotification(ne.key, ne.notification); + mCallbacks.addNotification((StatusBarNotification) msg.obj); break; } case MSG_UPDATE_NOTIFICATION: { - final NotificationQueueEntry ne = (NotificationQueueEntry)msg.obj; - mCallbacks.updateNotification(ne.key, ne.notification); + mCallbacks.updateNotification((StatusBarNotification) msg.obj); break; } case MSG_REMOVE_NOTIFICATION: { - mCallbacks.removeNotification((IBinder)msg.obj); + mCallbacks.removeNotification((String) msg.obj); break; } case MSG_DISABLE: diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java index a42c194..843db04 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java @@ -18,8 +18,8 @@ package com.android.systemui.statusbar; import android.content.Context; import android.graphics.Outline; +import android.graphics.RectF; import android.util.AttributeSet; -import android.widget.FrameLayout; /** * Like {@link ExpandableView}, but setting an outline for the height and clipping. @@ -27,9 +27,12 @@ import android.widget.FrameLayout; public abstract class ExpandableOutlineView extends ExpandableView { private final Outline mOutline = new Outline(); + private boolean mCustomOutline; + private float mDensity; public ExpandableOutlineView(Context context, AttributeSet attrs) { super(context, attrs); + mDensity = getResources().getDisplayMetrics().density; } @Override @@ -50,11 +53,37 @@ public abstract class ExpandableOutlineView extends ExpandableView { updateOutline(); } - private void updateOutline() { - mOutline.setRect(0, - mClipTopAmount, - getWidth(), - Math.max(mActualHeight, mClipTopAmount)); + protected void setOutlineRect(RectF rect) { + if (rect != null) { + setOutlineRect(rect.left, rect.top, rect.right, rect.bottom); + } else { + mCustomOutline = false; + updateOutline(); + } + } + + protected void setOutlineRect(float left, float top, float right, float bottom) { + mCustomOutline = true; + + int rectLeft = (int) left; + int rectTop = (int) top; + int rectRight = (int) right; + int rectBottom = (int) bottom; + + // Outlines need to be at least 1 dp + rectBottom = (int) Math.max(top + mDensity, rectBottom); + rectRight = (int) Math.max(left + mDensity, rectRight); + mOutline.setRect(rectLeft, rectTop, rectRight, rectBottom); setOutline(mOutline); } + + private void updateOutline() { + if (!mCustomOutline) { + mOutline.setRect(0, + mClipTopAmount, + getWidth(), + Math.max(mActualHeight, mClipTopAmount)); + setOutline(mOutline); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java index eaaac10..088f076 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java @@ -205,6 +205,21 @@ public abstract class ExpandableView extends FrameLayout { } /** + * Perform a remove animation on this view. + * + * @param translationDirection The direction value from [-1 ... 1] indicating in which the + * animation should be performed. A value of -1 means that The + * remove animation should be performed upwards, + * such that the child appears to be going away to the top. 1 + * Should mean the opposite. + * @param onFinishedRunnable A runnable which should be run when the animation is finished. + */ + public abstract void performRemoveAnimation(float translationDirection, + Runnable onFinishedRunnable); + + public abstract void performAddAnimation(long delay); + + /** * A listener notifying when {@link #getActualHeight} changes. */ public interface OnHeightChangedListener { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java index 8440b9f..0555879 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java @@ -18,8 +18,6 @@ package com.android.systemui.statusbar; import android.app.Notification; import android.content.Context; -import android.os.Binder; -import android.os.IBinder; import android.os.Process; import android.provider.Settings; import android.service.notification.StatusBarNotification; @@ -33,13 +31,14 @@ 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; - private final ArrayMap<IBinder, StatusBarNotification> mIntercepted - = new ArrayMap<IBinder, StatusBarNotification>(); + private final ArrayMap<String, StatusBarNotification> mIntercepted + = new ArrayMap<String, StatusBarNotification>(); - private Binder mSynKey; + private String mSynKey; public InterceptedNotifications(Context context, PhoneStatusBar bar) { mContext = context; @@ -49,36 +48,35 @@ public class InterceptedNotifications { public void releaseIntercepted() { final int n = mIntercepted.size(); for (int i = 0; i < n; i++) { - final IBinder key = mIntercepted.keyAt(i); final StatusBarNotification sbn = mIntercepted.valueAt(i); sbn.getNotification().extras.putBoolean(EXTRA_INTERCEPT, false); - mBar.addNotification(key, sbn); + mBar.addNotificationInternal(sbn); } mIntercepted.clear(); updateSyntheticNotification(); } - public boolean tryIntercept(IBinder key, StatusBarNotification notification) { + public boolean tryIntercept(StatusBarNotification notification) { if (!notification.getNotification().extras.getBoolean(EXTRA_INTERCEPT)) return false; if (shouldDisplayIntercepted()) return false; - mIntercepted.put(key, notification); + mIntercepted.put(notification.getKey(), notification); updateSyntheticNotification(); return true; } - public void remove(IBinder key) { + public void remove(String key) { if (mIntercepted.remove(key) != null) { updateSyntheticNotification(); } } public boolean isSyntheticEntry(Entry ent) { - return mSynKey != null && ent.key.equals(mSynKey); + return ent.key.equals(SYNTHETIC_KEY); } - public void update(IBinder key, StatusBarNotification notification) { - if (mIntercepted.containsKey(key)) { - mIntercepted.put(key, notification); + public void update(StatusBarNotification notification) { + if (mIntercepted.containsKey(notification.getKey())) { + mIntercepted.put(notification.getKey(), notification); } } @@ -90,7 +88,7 @@ public class InterceptedNotifications { private void updateSyntheticNotification() { if (mIntercepted.isEmpty()) { if (mSynKey != null) { - mBar.removeNotification(mSynKey); + mBar.removeNotificationInternal(mSynKey); mSynKey = null; } return; @@ -108,10 +106,10 @@ public class InterceptedNotifications { TAG.hashCode(), TAG, Process.myUid(), Process.myPid(), 0, n, mBar.getCurrentUserHandle()); if (mSynKey == null) { - mSynKey = new Binder(); - mBar.addNotification(mSynKey, sbn); + mSynKey = sbn.getKey(); + mBar.addNotificationInternal(sbn); } else { - mBar.updateNotification(mSynKey, sbn); + mBar.updateNotificationInternal(sbn); } final NotificationData.Entry entry = mBar.mNotificationData.findByKey(mSynKey); entry.row.setOnClickListener(mSynClickListener); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java index 3c080fe..1c2ca91 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java @@ -34,7 +34,6 @@ public class NotificationBackgroundView extends View { public NotificationBackgroundView(Context context, AttributeSet attrs) { super(context, attrs); - setWillNotDraw(false); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java index b1a5750..5696246 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar; -import android.os.IBinder; import android.service.notification.StatusBarNotification; import android.view.View; import android.widget.ImageView; @@ -29,7 +28,7 @@ import java.util.Comparator; */ public class NotificationData { public static final class Entry { - public IBinder key; + public String key; public StatusBarNotification notification; public StatusBarIconView icon; public ExpandableNotificationRow row; // the outer expanded view @@ -39,8 +38,8 @@ public class NotificationData { public View expandedBig; private boolean interruption; public Entry() {} - public Entry(IBinder key, StatusBarNotification n, StatusBarIconView ic) { - this.key = key; + public Entry(StatusBarNotification n, StatusBarIconView ic) { + this.key = n.getKey(); this.notification = n; this.icon = ic; } @@ -63,6 +62,7 @@ public class NotificationData { interruption = true; } } + private final ArrayList<Entry> mEntries = new ArrayList<Entry>(); private final Comparator<Entry> mEntryCmp = new Comparator<Entry>() { // sort first by score, then by when @@ -88,9 +88,9 @@ public class NotificationData { return mEntries.get(i); } - public Entry findByKey(IBinder key) { + public Entry findByKey(String key) { for (Entry e : mEntries) { - if (e.key == key) { + if (e.key.equals(key)) { return e; } } @@ -100,7 +100,7 @@ public class NotificationData { public int add(Entry entry) { int i; int N = mEntries.size(); - for (i=0; i<N; i++) { + for (i = 0; i < N; i++) { if (mEntryCmp.compare(mEntries.get(i), entry) > 0) { break; } @@ -109,7 +109,7 @@ public class NotificationData { return i; } - public Entry remove(IBinder key) { + public Entry remove(String key) { Entry e = findByKey(key); if (e != null) { mEntries.remove(e); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java index 8ae503a..a84daef 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java @@ -103,7 +103,11 @@ public class SpeedBumpView extends ExpandableView implements View.OnClickListene @Override public int getIntrinsicHeight() { - return getActualHeight(); + if (mCurrentAnimator != null) { + // expand animation is running + return getActualHeight(); + } + return mIsExpanded ? getHeight() : mCollapsedHeight; } @Override @@ -184,7 +188,7 @@ public class SpeedBumpView extends ExpandableView implements View.OnClickListene } public void performVisibilityAnimation(boolean nowVisible) { - animateDivider(nowVisible); + animateDivider(nowVisible, null /* onFinishedRunnable */); // Animate explanation Text if (mIsExpanded) { @@ -192,7 +196,14 @@ public class SpeedBumpView extends ExpandableView implements View.OnClickListene } } - public void animateDivider(boolean nowVisible) { + /** + * Animate the divider to a new visibility. + * + * @param nowVisible should it now be visible + * @param onFinishedRunnable A runnable which should be run when the animation is + * finished. + */ + public void animateDivider(boolean nowVisible, Runnable onFinishedRunnable) { if (nowVisible != mDividerVisible) { // Animate dividers float endValue = nowVisible ? 1.0f : 0.0f; @@ -204,7 +215,8 @@ public class SpeedBumpView extends ExpandableView implements View.OnClickListene .scaleX(endValue) .scaleY(endValue) .translationX(endTranslationXLeft) - .setInterpolator(mFastOutSlowInInterpolator); + .setInterpolator(mFastOutSlowInInterpolator) + .withEndAction(onFinishedRunnable); mLineRight.animate() .alpha(endValue) .withLayer() @@ -216,6 +228,10 @@ public class SpeedBumpView extends ExpandableView implements View.OnClickListene // Animate dots mDots.performVisibilityAnimation(nowVisible); mDividerVisible = nowVisible; + } else { + if (onFinishedRunnable != null) { + onFinishedRunnable.run(); + } } } @@ -250,6 +266,16 @@ public class SpeedBumpView extends ExpandableView implements View.OnClickListene } } + @Override + public void performRemoveAnimation(float translationDirection, Runnable onFinishedRunnable) { + performVisibilityAnimation(false); + } + + @Override + public void performAddAnimation(long delay) { + performVisibilityAnimation(true); + } + private void resetExplanationText() { mExplanationText.setTranslationY(0); mExplanationText.setVisibility(INVISIBLE); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index d8e1766..2fa2a00 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -28,6 +28,7 @@ import com.android.keyguard.R; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.keyguard.KeyguardViewMediator; +import static com.android.keyguard.KeyguardHostView.OnDismissAction; import static com.android.keyguard.KeyguardSecurityModel.*; /** @@ -64,11 +65,16 @@ public class KeyguardBouncer { // Keyguard. If we need to authenticate, show the bouncer. if (!mKeyguardView.dismiss()) { mRoot.setVisibility(View.VISIBLE); - mKeyguardView.requestFocus(); mKeyguardView.onResume(); } } + public void showWithDismissAction(OnDismissAction r) { + ensureView(); + mKeyguardView.setOnDismissAction(r); + show(); + } + public void hide() { if (mKeyguardView != null) { mKeyguardView.cleanUp(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java new file mode 100644 index 0000000..6a83a5e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java @@ -0,0 +1,186 @@ +/* + * 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.systemui.statusbar.phone; + +import android.content.res.Resources; +import android.graphics.Path; +import android.view.animation.PathInterpolator; + +import com.android.systemui.R; + +/** + * Utility class to calculate the clock position and top padding of notifications on Keyguard. + */ +public class KeyguardClockPositionAlgorithm { + + private static final float SLOW_DOWN_FACTOR = 0.4f; + + private static final float CLOCK_RUBBERBAND_FACTOR_MIN = 0.08f; + private static final float CLOCK_RUBBERBAND_FACTOR_MAX = 0.8f; + + private static final float CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MIN = 1.4f; + private static final float CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MAX = 3.2f; + + private int mClockNotificationsMarginMin; + private int mClockNotificationsMarginMax; + private float mClockYFractionMin; + private float mClockYFractionMax; + private int mMaxKeyguardNotifications; + private int mMaxPanelHeight; + private float mExpandedHeight; + private int mNotificationCount; + private int mHeight; + private int mKeyguardStatusHeight; + + /** + * The number (fractional) of notifications the "more" card counts when calculating how many + * notifications are currently visible for the y positioning of the clock. + */ + private float mMoreCardNotificationAmount; + + private static final PathInterpolator sSlowDownInterpolator; + + static { + Path path = new Path(); + path.moveTo(0, 0); + path.cubicTo(0.3f, 0.875f, 0.6f, 1f, 1f, 1f); + sSlowDownInterpolator = new PathInterpolator(path); + } + + /** + * Refreshes the dimension values. + */ + public void loadDimens(Resources res) { + mClockNotificationsMarginMin = res.getDimensionPixelSize( + R.dimen.keyguard_clock_notifications_margin_min); + mClockNotificationsMarginMax = res.getDimensionPixelSize( + R.dimen.keyguard_clock_notifications_margin_max); + mClockYFractionMin = res.getFraction(R.fraction.keyguard_clock_y_fraction_min, 1, 1); + mClockYFractionMax = res.getFraction(R.fraction.keyguard_clock_y_fraction_max, 1, 1); + mMoreCardNotificationAmount = + (float) res.getDimensionPixelSize(R.dimen.notification_summary_height) / + res.getDimensionPixelSize(R.dimen.notification_min_height); + } + + public void setup(int maxKeyguardNotifications, int maxPanelHeight, float expandedHeight, + int notificationCount, int height, int keyguardStatusHeight) { + mMaxKeyguardNotifications = maxKeyguardNotifications; + mMaxPanelHeight = maxPanelHeight; + mExpandedHeight = expandedHeight; + mNotificationCount = notificationCount; + mHeight = height; + mKeyguardStatusHeight = keyguardStatusHeight; + } + + public void run(Result result) { + int y = getClockY() - mKeyguardStatusHeight/2; + float clockAdjustment = getClockYExpansionAdjustment(); + float topPaddingAdjMultiplier = getTopPaddingAdjMultiplier(); + result.stackScrollerPaddingAdjustment = (int) (clockAdjustment*topPaddingAdjMultiplier); + int clockNotificationsPadding = getClockNotificationsPadding() + + result.stackScrollerPaddingAdjustment; + int padding = y + clockNotificationsPadding; + y += clockAdjustment; + result.clockY = y; + result.stackScrollerPadding = mKeyguardStatusHeight + padding; + result.clockAlpha = getClockAlpha(result.stackScrollerPadding + - (y + mKeyguardStatusHeight)); + } + + private int getClockNotificationsPadding() { + float t = getNotificationAmountT(); + t = Math.min(t, 1.0f); + return (int) (t * mClockNotificationsMarginMin + (1 - t) * mClockNotificationsMarginMax); + } + + private float getClockYFraction() { + float t = getNotificationAmountT(); + t = Math.min(t, 1.0f); + return (1 - t) * mClockYFractionMax + t * mClockYFractionMin; + } + + private int getClockY() { + return (int) (getClockYFraction() * mHeight); + } + + private float getClockYExpansionAdjustment() { + float rubberbandFactor = getClockYExpansionRubberbandFactor(); + float value = (rubberbandFactor * (mMaxPanelHeight - mExpandedHeight)); + float t = value / mMaxPanelHeight; + float slowedDownValue = -sSlowDownInterpolator.getInterpolation(t) * SLOW_DOWN_FACTOR + * mMaxPanelHeight; + if (mNotificationCount == 0) { + return (-2*value + slowedDownValue)/3; + } else { + return slowedDownValue; + } + } + + private float getClockYExpansionRubberbandFactor() { + float t = getNotificationAmountT(); + t = Math.min(t, 1.0f); + t = (float) Math.pow(t, 0.3f); + return (1 - t) * CLOCK_RUBBERBAND_FACTOR_MAX + t * CLOCK_RUBBERBAND_FACTOR_MIN; + } + + private float getTopPaddingAdjMultiplier() { + float t = getNotificationAmountT(); + t = Math.min(t, 1.0f); + return (1 - t) * CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MIN + + t * CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MAX; + } + + private float getClockAlpha(int clockNotificationPadding) { + float t = getNotificationAmountT(); + t = (float) Math.pow(t, 0.3f); + float multiplier = 1 + 2 * (1 - t); + float alpha = 1 + (float) clockNotificationPadding * multiplier / mKeyguardStatusHeight * 3; + return Math.max(0, Math.min(1, alpha)); + } + + /** + * @return a value from 0 to 1 depending on how many notification there are + */ + private float getNotificationAmountT() { + return mNotificationCount + / (mMaxKeyguardNotifications + mMoreCardNotificationAmount); + } + + public static class Result { + + /** + * The y translation of the clock. + */ + public int clockY; + + /** + * The alpha value of the clock. + */ + public float clockAlpha; + + /** + * The top padding of the stack scroller, in pixels. + */ + public int stackScrollerPadding; + + /** + * The top padding adjustment of the stack scroller, in pixels. This value is used to adjust + * the padding, but not the overall panel size. + */ + public int stackScrollerPaddingAdjustment; + } +} 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 123a4f0..f5252a3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -19,7 +19,6 @@ package com.android.systemui.statusbar.phone; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; -import android.animation.PropertyValuesHolder; import android.animation.ValueAnimator; import android.content.Context; import android.util.AttributeSet; @@ -48,6 +47,7 @@ public class NotificationPanelView extends PanelView implements PhoneStatusBar mStatusBar; private StatusBarHeaderView mHeader; private View mQsContainer; + private View mQsPanel; private View mKeyguardStatusView; private ObservableScrollView mScrollView; private View mStackScrollerContainer; @@ -66,6 +66,7 @@ public class NotificationPanelView extends PanelView implements */ private boolean mIntercepting; private boolean mQsExpanded; + private boolean mKeyguardShowing; private float mInitialHeightOnTouch; private float mInitialTouchX; private float mInitialTouchY; @@ -75,6 +76,7 @@ public class NotificationPanelView extends PanelView implements private int mQsMinExpansionHeight; private int mQsMaxExpansionHeight; private int mMinStackHeight; + private int mQsPeekHeight; private float mNotificationTranslation; private int mStackScrollerIntrinsicPadding; private boolean mQsExpansionEnabled = true; @@ -82,19 +84,14 @@ public class NotificationPanelView extends PanelView implements private FlingAnimationUtils mFlingAnimationUtils; private int mStatusBarMinHeight; - private int mClockNotificationsMarginMin; - private int mClockNotificationsMarginMax; - private float mClockYFractionMin; - private float mClockYFractionMax; private Interpolator mFastOutSlowInInterpolator; private ObjectAnimator mClockAnimator; private int mClockAnimationTarget = -1; - - /** - * The number (fractional) of notifications the "more" card counts when calculating how many - * notifications are currently visible for the y positioning of the clock. - */ - private float mMoreCardNotificationAmount; + private int mTopPaddingAdjustment; + private KeyguardClockPositionAlgorithm mClockPositionAlgorithm = + new KeyguardClockPositionAlgorithm(); + private KeyguardClockPositionAlgorithm.Result mClockPositionResult = + new KeyguardClockPositionAlgorithm.Result(); public NotificationPanelView(Context context, AttributeSet attrs) { super(context, attrs); @@ -124,6 +121,7 @@ public class NotificationPanelView extends PanelView implements mKeyguardStatusView = findViewById(R.id.keyguard_status_view); mStackScrollerContainer = findViewById(R.id.notification_container_parent); mQsContainer = findViewById(R.id.quick_settings_container); + mQsPanel = findViewById(R.id.quick_settings_panel); mScrollView = (ObservableScrollView) findViewById(R.id.scroll_view); mScrollView.setListener(this); mNotificationStackScroller = (NotificationStackScrollLayout) @@ -139,34 +137,24 @@ public class NotificationPanelView extends PanelView implements mNotificationTopPadding = getResources().getDimensionPixelSize( R.dimen.notifications_top_padding); mMinStackHeight = getResources().getDimensionPixelSize(R.dimen.collapsed_stack_height); - mClockNotificationsMarginMin = getResources().getDimensionPixelSize( - R.dimen.keyguard_clock_notifications_margin_min); - mClockNotificationsMarginMax = getResources().getDimensionPixelSize( - R.dimen.keyguard_clock_notifications_margin_max); - mClockYFractionMin = - getResources().getFraction(R.fraction.keyguard_clock_y_fraction_min, 1, 1); - mClockYFractionMax = - getResources().getFraction(R.fraction.keyguard_clock_y_fraction_max, 1, 1); - mMoreCardNotificationAmount = - (float) getResources().getDimensionPixelSize(R.dimen.notification_summary_height) / - getResources().getDimensionPixelSize(R.dimen.notification_min_height); mFlingAnimationUtils = new FlingAnimationUtils(getContext(), 0.4f); mStatusBarMinHeight = getResources().getDimensionPixelSize( com.android.internal.R.dimen.status_bar_height); + mQsPeekHeight = getResources().getDimensionPixelSize(R.dimen.qs_peek_height); + mClockPositionAlgorithm.loadDimens(getResources()); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); - if (!mQsExpanded) { - positionClockAndNotifications(); - } // Calculate quick setting heights. - mQsMinExpansionHeight = mHeader.getCollapsedHeight(); + mQsMinExpansionHeight = mHeader.getCollapsedHeight() + mQsPeekHeight; mQsMaxExpansionHeight = mHeader.getExpandedHeight() + mQsContainer.getHeight(); - if (mQsExpansionHeight == 0) { - mQsExpansionHeight = mQsMinExpansionHeight; + if (!mQsExpanded) { + setQsExpansion(mQsMinExpansionHeight); + positionClockAndNotifications(); + mNotificationStackScroller.setStackHeight(getExpandedHeight()); } } @@ -177,17 +165,26 @@ public class NotificationPanelView extends PanelView implements private void positionClockAndNotifications() { boolean animateClock = mNotificationStackScroller.isAddOrRemoveAnimationPending(); if (mStatusBar.getBarState() != StatusBarState.KEYGUARD) { - mStackScrollerIntrinsicPadding = mHeader.getBottom() + mNotificationTopPadding; + mStackScrollerIntrinsicPadding = mHeader.getBottom() + mQsPeekHeight + + mNotificationTopPadding; + mTopPaddingAdjustment = 0; } else { - int notificationCount = mNotificationStackScroller.getNotGoneChildCount(); - int y = getClockY(notificationCount) - mKeyguardStatusView.getHeight()/2; - int padding = getClockNotificationsPadding(notificationCount); + mClockPositionAlgorithm.setup( + mStatusBar.getMaxKeyguardNotifications(), + getMaxPanelHeight(), + getExpandedHeight(), + mNotificationStackScroller.getNotGoneChildCount(), + getHeight(), + mKeyguardStatusView.getHeight()); + mClockPositionAlgorithm.run(mClockPositionResult); if (animateClock || mClockAnimator != null) { - startClockAnimation(y); + startClockAnimation(mClockPositionResult.clockY); } else { - mKeyguardStatusView.setY(y); + mKeyguardStatusView.setY(mClockPositionResult.clockY); } - mStackScrollerIntrinsicPadding = y + mKeyguardStatusView.getHeight() + padding; + applyClockAlpha(mClockPositionResult.clockAlpha); + mStackScrollerIntrinsicPadding = mClockPositionResult.stackScrollerPadding; + mTopPaddingAdjustment = mClockPositionResult.stackScrollerPaddingAdjustment; } mNotificationStackScroller.setTopPadding(mStackScrollerIntrinsicPadding, mAnimateNextTopPaddingChange || animateClock); @@ -218,28 +215,19 @@ public class NotificationPanelView extends PanelView implements mClockAnimationTarget = -1; } }); - StackStateAnimator.startInstantly(mClockAnimator); + mClockAnimator.start(); return true; } }); } - private int getClockNotificationsPadding(int notificationCount) { - float t = notificationCount - / (mStatusBar.getMaxKeyguardNotifications() + mMoreCardNotificationAmount); - t = Math.min(t, 1.0f); - return (int) (t * mClockNotificationsMarginMin + (1 - t) * mClockNotificationsMarginMax); - } - - private float getClockYFraction(int notificationCount) { - float t = notificationCount - / (mStatusBar.getMaxKeyguardNotifications() + mMoreCardNotificationAmount); - t = Math.min(t, 1.0f); - return (1 - t) * mClockYFractionMax + t * mClockYFractionMin; - } - - private int getClockY(int notificationCount) { - return (int) (getClockYFraction(notificationCount) * getHeight()); + private void applyClockAlpha(float alpha) { + if (alpha != 1.0f) { + mKeyguardStatusView.setLayerType(LAYER_TYPE_HARDWARE, null); + } else { + mKeyguardStatusView.setLayerType(LAYER_TYPE_NONE, null); + } + mKeyguardStatusView.setAlpha(alpha); } public void animateToFullShade() { @@ -366,10 +354,12 @@ public class NotificationPanelView extends PanelView implements @Override public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { - // Block request so we can still intercept the scrolling when QS is expanded. - if (!mQsExpanded) { - super.requestDisallowInterceptTouchEvent(disallowIntercept); + // Block request when interacting with the scroll view so we can still intercept the + // scrolling when QS is expanded. + if (mScrollView.isDispatchingTouchEvent()) { + return; } + super.requestDisallowInterceptTouchEvent(disallowIntercept); } private void flingWithCurrentVelocity() { @@ -466,31 +456,41 @@ public class NotificationPanelView extends PanelView implements setQsExpansion(height); } - private void expandQs() { - mHeader.setExpanded(true); - mNotificationStackScroller.setEnabled(false); - mScrollView.setVisibility(View.VISIBLE); - mQsExpanded = true; + private void setQsExpanded(boolean expanded) { + boolean changed = mQsExpanded != expanded; + if (changed) { + mQsExpanded = expanded; + updateQsState(); + } + } + + public void setKeyguardShowing(boolean keyguardShowing) { + mKeyguardShowing = keyguardShowing; + updateQsState(); } - private void collapseQs() { - mHeader.setExpanded(false); - mNotificationStackScroller.setEnabled(true); - mScrollView.setVisibility(View.INVISIBLE); - mQsExpanded = false; + private void updateQsState() { + mHeader.setExpanded(mQsExpanded); + mNotificationStackScroller.setEnabled(!mQsExpanded); + mQsPanel.setVisibility(mQsExpanded ? View.VISIBLE : View.INVISIBLE); + mQsContainer.setVisibility(mKeyguardShowing && !mQsExpanded + ? View.INVISIBLE + : View.VISIBLE); + mScrollView.setTouchEnabled(mQsExpanded); } private void setQsExpansion(float height) { height = Math.min(Math.max(height, mQsMinExpansionHeight), mQsMaxExpansionHeight); if (height > mQsMinExpansionHeight && !mQsExpanded) { - expandQs(); + setQsExpanded(true); } else if (height <= mQsMinExpansionHeight && mQsExpanded) { - collapseQs(); + setQsExpanded(false); } mQsExpansionHeight = height; - mHeader.setExpansion(height); + mHeader.setExpansion(height - mQsPeekHeight); setQsTranslation(height); setQsStackScrollerPadding(height); + mStatusBar.userActivity(); } private void setQsTranslation(float height) { @@ -626,7 +626,7 @@ public class NotificationPanelView extends PanelView implements int emptyBottomMargin = mStackScrollerContainer.getHeight() - mNotificationStackScroller.getHeight() + mNotificationStackScroller.getEmptyBottomMargin(); - int maxHeight = maxPanelHeight - emptyBottomMargin; + int maxHeight = maxPanelHeight - emptyBottomMargin - mTopPaddingAdjustment; maxHeight = Math.max(maxHeight, mStatusBarMinHeight); return maxHeight; } @@ -637,6 +637,9 @@ public class NotificationPanelView extends PanelView implements @Override protected void onHeightUpdated(float expandedHeight) { + if (!mQsExpanded) { + positionClockAndNotifications(); + } mNotificationStackScroller.setStackHeight(expandedHeight); } @@ -653,6 +656,23 @@ public class NotificationPanelView extends PanelView implements } @Override + protected void onOverExpansionChanged(float overExpansion) { + float currentOverScroll = mNotificationStackScroller.getCurrentOverScrolledPixels(true); + mNotificationStackScroller.setOverScrolledPixels(currentOverScroll + overExpansion + - mOverExpansion, true /* onTop */, false /* animate */); + super.onOverExpansionChanged(overExpansion); + } + + @Override + protected void onTrackingStopped() { + super.onTrackingStopped(); + mOverExpansion = 0.0f; + mNotificationStackScroller.setOverScrolledPixels(0.0f, true /* onTop */, + true /* animate */); + } + + + @Override public void onHeightChanged(ExpandableView view) { requestPanelHeightUpdate(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java index ba0b66e..ea5b309 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone; import android.content.Context; import android.util.AttributeSet; +import android.view.MotionEvent; import android.view.View; import android.widget.ScrollView; @@ -28,6 +29,8 @@ public class ObservableScrollView extends ScrollView { private Listener mListener; private int mLastOverscrollAmount; + private boolean mDispatchingTouchEvent; + private boolean mTouchEnabled = true; public ObservableScrollView(Context context, AttributeSet attrs) { super(context, attrs); @@ -37,10 +40,18 @@ public class ObservableScrollView extends ScrollView { mListener = listener; } + public void setTouchEnabled(boolean touchEnabled) { + mTouchEnabled = touchEnabled; + } + public boolean isScrolledToBottom() { return getScrollY() == getMaxScrollY(); } + public boolean isDispatchingTouchEvent() { + return mDispatchingTouchEvent; + } + private int getMaxScrollY() { int scrollRange = 0; if (getChildCount() > 0) { @@ -52,6 +63,17 @@ public class ObservableScrollView extends ScrollView { } @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + if (!mTouchEnabled) { + return false; + } + mDispatchingTouchEvent = true; + boolean result = super.dispatchTouchEvent(ev); + mDispatchingTouchEvent = false; + return result; + } + + @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); if (mListener != null) { 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 b6a43a7..7c1f2cf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java @@ -38,6 +38,7 @@ import java.io.PrintWriter; public class PanelView extends FrameLayout { public static final boolean DEBUG = PanelBar.DEBUG; public static final String TAG = PanelView.class.getSimpleName(); + protected float mOverExpansion; private final void logf(String fmt, Object... args) { Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args)); @@ -402,7 +403,12 @@ public class PanelView extends FrameLayout { public void setExpandedHeightInternal(float h) { float fh = getMaxPanelHeight(); - mExpandedHeight = h; + mExpandedHeight = Math.min(fh, h); + float overExpansion = h - fh; + overExpansion = Math.max(0, overExpansion); + if (overExpansion != mOverExpansion) { + onOverExpansionChanged(overExpansion); + } if (DEBUG) { logf("setExpansion: height=%.1f fh=%.1f tracking=%s", h, fh, mTracking ? "T" : "f"); @@ -412,6 +418,10 @@ public class PanelView extends FrameLayout { mExpandedFraction = Math.min(1f, (fh == 0) ? 0 : h / fh); } + protected void onOverExpansionChanged(float overExpansion) { + mOverExpansion = overExpansion; + } + protected void onHeightUpdated(float expandedHeight) { requestLayout(); } 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 54af2c5..28367d0e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -22,6 +22,7 @@ import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN; import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN; import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; import static android.app.StatusBarManager.windowStateToString; +import static com.android.keyguard.KeyguardHostView.OnDismissAction; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT; @@ -1027,18 +1028,19 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, return new UserHandle(mCurrentUserId); } - public void addNotification(IBinder key, StatusBarNotification notification) { + @Override + public void addNotificationInternal(StatusBarNotification notification) { if (DEBUG) Log.d(TAG, "addNotification score=" + notification.getScore()); - Entry shadeEntry = createNotificationViews(key, notification); + Entry shadeEntry = createNotificationViews(notification); if (shadeEntry == null) { return; } - if (mZenMode != Global.ZEN_MODE_OFF && mIntercepted.tryIntercept(key, notification)) { + if (mZenMode != Global.ZEN_MODE_OFF && mIntercepted.tryIntercept(notification)) { return; } if (mUseHeadsUp && shouldInterrupt(notification)) { if (DEBUG) Log.d(TAG, "launching notification in heads up mode"); - Entry interruptionCandidate = new Entry(key, notification, null); + Entry interruptionCandidate = new Entry(notification, null); ViewGroup holder = mHeadsUpNotificationView.getHolder(); if (inflateViewsForHeadsUp(interruptionCandidate, holder)) { mInterruptingNotificationTime = System.currentTimeMillis(); @@ -1070,7 +1072,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, // show the ticker if there isn't already a heads up if (mInterruptingNotificationEntry == null) { - tick(null, notification, true); + tick(notification, true); } } addNotificationViews(shadeEntry); @@ -1089,12 +1091,13 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } @Override - public void updateNotification(IBinder key, StatusBarNotification notification) { - super.updateNotification(key, notification); - mIntercepted.update(key, notification); + public void updateNotification(StatusBarNotification notification) { + super.updateNotification(notification); + mIntercepted.update(notification); } - public void removeNotification(IBinder key) { + @Override + public void removeNotificationInternal(String key) { StatusBarNotification old = removeNotificationViews(key); if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old); @@ -2019,7 +2022,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, public void setHardKeyboardStatus(boolean available, boolean enabled) {} @Override - protected void tick(IBinder key, StatusBarNotification n, boolean firstTime) { + protected void tick(StatusBarNotification n, boolean firstTime) { // no ticking in lights-out mode if (!areLightsOn()) return; @@ -2344,6 +2347,15 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } }; + @Override + protected void startNotificationActivity(OnDismissAction action) { + if (mStatusBarKeyguardViewManager.isShowing()) { + mStatusBarKeyguardViewManager.dismissWithAction(action); + } else { + action.onDismiss(); + } + } + // SystemUIService notifies SystemBars of configuration changes, which then calls down here @Override protected void onConfigurationChanged(Configuration newConfig) { @@ -2758,14 +2770,17 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { mKeyguardBottomArea.setVisibility(View.VISIBLE); mHeader.setKeyguardShowing(true); + mNotificationPanel.setKeyguardShowing(true); } else { mKeyguardBottomArea.setVisibility(View.GONE); mHeader.setKeyguardShowing(false); + mNotificationPanel.setKeyguardShowing(false); } updateStackScrollerState(); updatePublicMode(); updateRowStates(); + updateSpeedBump(); checkBarModes(); updateNotificationIcons(); updateCarrierLabelVisibility(false); @@ -2776,9 +2791,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } public void userActivity() { - if (mState == StatusBarState.KEYGUARD) { - mKeyguardViewMediatorCallback.userActivity(); - } + mHandler.removeCallbacks(mUserActivity); + mHandler.post(mUserActivity); } public boolean interceptMediaKey(KeyEvent event) { @@ -2936,4 +2950,13 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, public void onScreenTurnedOn() { mStackScroller.setAnimationsEnabled(true); } + + private final Runnable mUserActivity = new Runnable() { + @Override + public void run() { + if (mState == StatusBarState.KEYGUARD) { + mKeyguardViewMediatorCallback.userActivity(); + } + } + }; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java index 389e725..3245f1a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java @@ -38,6 +38,11 @@ import com.android.systemui.statusbar.policy.UserInfoController; */ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickListener { + /** + * How much the header expansion gets rubberbanded while expanding the panel. + */ + private static final float EXPANSION_RUBBERBAND_FACTOR = 0.35f; + private boolean mExpanded; private boolean mKeyguardShowing; @@ -128,6 +133,8 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL updateVisibilities(); updateSystemIconsLayoutParams(); updateBrightnessControllerState(); + updateZTranslation(); + updateClickTargets(); if (mQSPanel != null) { mQSPanel.setExpanded(expanded); } @@ -202,18 +209,30 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL } } + private void updateClickTargets() { + mDateTime.setClickable(mExpanded); + mMultiUserSwitch.setClickable(mExpanded); + } + + private void updateZTranslation() { + + // If we are on the Keyguard, we need to set our z position to zero, so we don't get + // shadows. + if (mKeyguardShowing && !mExpanded) { + setZ(0); + } else { + setTranslationZ(0); + } + } + public void setExpansion(float height) { + height = (height - mCollapsedHeight) * EXPANSION_RUBBERBAND_FACTOR + mCollapsedHeight; if (height < mCollapsedHeight) { height = mCollapsedHeight; } if (height > mExpandedHeight) { height = mExpandedHeight; } - if (mExpanded) { - mBackground.setTranslationY(-(mExpandedHeight - height)); - } else { - mBackground.setTranslationY(0); - } setClipping(height); } @@ -247,14 +266,10 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL public void setKeyguardShowing(boolean keyguardShowing) { mKeyguardShowing = keyguardShowing; - if (keyguardShowing) { - setZ(0); - } else { - setTranslationZ(0); - } updateHeights(); updateWidth(); updateVisibilities(); + updateZTranslation(); } public void setUserInfoController(UserInfoController userInfoController) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 1040c15..3849d8d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -29,6 +29,8 @@ import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.ViewMediatorCallback; +import static com.android.keyguard.KeyguardHostView.OnDismissAction; + /** * Manages creating, showing, hiding and resetting the keyguard within the status bar. Calls back * via {@link ViewMediatorCallback} to poke the wake lock and report that the keyguard is done, @@ -108,6 +110,13 @@ public class StatusBarKeyguardViewManager { updateStates(); } + public void dismissWithAction(OnDismissAction r) { + if (!mOccluded) { + mBouncer.showWithDismissAction(r); + } + updateStates(); + } + /** * Reset the state of the view. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java index 173af40..3ce6905 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java @@ -166,7 +166,7 @@ public final class UserInfoController { if (rawAvatar != null) { avatar = new BitmapDrawable(mContext.getResources(), circularClip(rawAvatar)); } else { - avatar = mContext.getResources().getDrawable(R.drawable.ic_qs_default_user); + avatar = mContext.getResources().getDrawable(R.drawable.ic_account_circle); mUseDefaultAvatar = true; } 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 41914ed..5e2d06b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java @@ -28,6 +28,7 @@ public class AnimationFilter { boolean animateScale; boolean animateHeight; boolean animateDimmed; + boolean hasDelays; public AnimationFilter animateAlpha() { animateAlpha = true; @@ -39,6 +40,11 @@ public class AnimationFilter { return this; } + public AnimationFilter hasDelays() { + hasDelays = true; + return this; + } + public AnimationFilter animateZ() { animateZ = true; return this; @@ -79,6 +85,7 @@ public class AnimationFilter { animateScale |= filter.animateScale; animateHeight |= filter.animateHeight; animateDimmed |= filter.animateDimmed; + hasDelays |= filter.hasDelays; } private void reset() { @@ -88,5 +95,6 @@ public class AnimationFilter { animateScale = false; animateHeight = 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 90f3d17..079b184 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -103,6 +103,7 @@ public class NotificationStackScrollLayout extends ViewGroup 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<AnimationEvent> mAnimationEvents = new ArrayList<AnimationEvent>(); private ArrayList<View> mSwipedOutViews = new ArrayList<View>(); @@ -969,9 +970,24 @@ public class NotificationStackScrollLayout extends ViewGroup } /** + * @return The first child which has visibility unequal to GONE which is currently below the + * given translationY or equal to it. + */ + private View getFirstChildBelowTranlsationY(float translationY) { + int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + if (child.getVisibility() != View.GONE && child.getTranslationY() >= translationY) { + return child; + } + } + return null; + } + + /** * @return the last child which has visibility unequal to GONE */ - private View getLastChildNotGone() { + public View getLastChildNotGone() { int childCount = getChildCount(); for (int i = childCount - 1; i >= 0; i--) { View child = getChildAt(i); @@ -1094,23 +1110,41 @@ public class NotificationStackScrollLayout extends ViewGroup @Override protected void onViewRemoved(View child) { super.onViewRemoved(child); + mStackScrollAlgorithm.notifyChildrenChanged(this); + if (mChildrenChangingPositions.contains(child)) { + // This is only a position change, don't do anything special + return; + } ((ExpandableView) child).setOnHeightChangedListener(null); mCurrentStackScrollState.removeViewStateForView(child); - mStackScrollAlgorithm.notifyChildrenChanged(this); updateScrollStateForRemovedChild(child); - generateRemoveAnimation(child); + boolean animationGenerated = generateRemoveAnimation(child); + if (animationGenerated && !mSwipedOutViews.contains(child)) { + // Add this view to an overlay in order to ensure that it will still be temporary + // drawn when removed + getOverlay().add(child); + } } - private void generateRemoveAnimation(View child) { + /** + * Generate a remove animation for a child view. + * + * @param child The view to generate the remove animation for. + * @return Whether an animation was generated. + */ + private boolean generateRemoveAnimation(View child) { if (mIsExpanded && mAnimationsEnabled) { if (!mChildrenToAddAnimated.contains(child)) { // Generate Animations mChildrenToRemoveAnimated.add(child); mNeedsAnimation = true; + return true; } else { mChildrenToAddAnimated.remove(child); + return false; } } + return false; } /** @@ -1155,9 +1189,7 @@ public class NotificationStackScrollLayout extends ViewGroup super.onViewAdded(child); mStackScrollAlgorithm.notifyChildrenChanged(this); ((ExpandableView) child).setOnHeightChangedListener(this); - if (child.getVisibility() != View.GONE) { - generateAddAnimation(child); - } + generateAddAnimation(child); } public void setAnimationsEnabled(boolean animationsEnabled) { @@ -1168,10 +1200,13 @@ public class NotificationStackScrollLayout extends ViewGroup return mNeedsAnimation && (!mChildrenToAddAnimated.isEmpty() || !mChildrenToRemoveAnimated.isEmpty()); } - + /** + * Generate an animation for an added child view. + * + * @param child The view to be added. + */ public void generateAddAnimation(View child) { - if (mIsExpanded && mAnimationsEnabled) { - + if (mIsExpanded && mAnimationsEnabled && !mChildrenChangingPositions.contains(child)) { // Generate Animations mChildrenToAddAnimated.add(child); mNeedsAnimation = true; @@ -1186,9 +1221,10 @@ public class NotificationStackScrollLayout extends ViewGroup */ public void changeViewPosition(View child, int newIndex) { if (child != null && child.getParent() == this) { + mChildrenChangingPositions.add(child); removeView(child); addView(child, newIndex); - // TODO: handle events + mNeedsAnimation = true; } } @@ -1197,16 +1233,18 @@ public class NotificationStackScrollLayout extends ViewGroup generateChildHierarchyEvents(); mNeedsAnimation = false; } - if (!mAnimationEvents.isEmpty()) { + if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) { mStateAnimator.startAnimationForEvents(mAnimationEvents, mCurrentStackScrollState); + mAnimationEvents.clear(); } else { applyCurrentState(); } } private void generateChildHierarchyEvents() { - generateChildAdditionEvents(); generateChildRemovalEvents(); + generateChildAdditionEvents(); + generatePositionChangeEvents(); generateSnapBackEvents(); generateDragEvents(); generateTopPaddingEvent(); @@ -1237,12 +1275,24 @@ public class NotificationStackScrollLayout extends ViewGroup int animationType = childWasSwipedOut ? AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT : AnimationEvent.ANIMATION_TYPE_REMOVE; - mAnimationEvents.add(new AnimationEvent(child, animationType)); + AnimationEvent event = new AnimationEvent(child, animationType); + + // we need to know the view after this one + event.viewAfterChangingView = getFirstChildBelowTranlsationY(child.getTranslationY()); + mAnimationEvents.add(event); } mSwipedOutViews.clear(); mChildrenToRemoveAnimated.clear(); } + private void generatePositionChangeEvents() { + for (View child : mChildrenChangingPositions) { + mAnimationEvents.add(new AnimationEvent(child, + AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION)); + } + mChildrenChangingPositions.clear(); + } + private void generateChildAdditionEvents() { for (View child : mChildrenToAddAnimated) { mAnimationEvents.add(new AnimationEvent(child, @@ -1467,7 +1517,6 @@ public class NotificationStackScrollLayout extends ViewGroup public void onChildAnimationFinished() { requestChildrenUpdate(); - mAnimationEvents.clear(); } /** @@ -1513,9 +1562,9 @@ public class NotificationStackScrollLayout extends ViewGroup } private void updateSpeedBump(boolean visible) { - int newVisibility = visible ? VISIBLE : GONE; - int oldVisibility = mSpeedBumpView.getVisibility(); - if (newVisibility != oldVisibility) { + boolean notGoneBefore = mSpeedBumpView.getVisibility() != GONE; + if (visible != notGoneBefore) { + int newVisibility = visible ? VISIBLE : GONE; mSpeedBumpView.setVisibility(newVisibility); if (visible) { mSpeedBumpView.collapse(); @@ -1551,21 +1600,24 @@ public class NotificationStackScrollLayout extends ViewGroup .animateAlpha() .animateHeight() .animateY() - .animateZ(), + .animateZ() + .hasDelays(), // ANIMATION_TYPE_REMOVE new AnimationFilter() .animateAlpha() .animateHeight() .animateY() - .animateZ(), + .animateZ() + .hasDelays(), // ANIMATION_TYPE_REMOVE_SWIPED_OUT new AnimationFilter() .animateAlpha() .animateHeight() .animateY() - .animateZ(), + .animateZ() + .hasDelays(), // ANIMATION_TYPE_TOP_PADDING_CHANGED new AnimationFilter() @@ -1593,16 +1645,23 @@ public class NotificationStackScrollLayout extends ViewGroup new AnimationFilter() .animateY() .animateScale() - .animateDimmed() + .animateDimmed(), + + // ANIMATION_TYPE_CHANGE_POSITION + new AnimationFilter() + .animateAlpha() + .animateHeight() + .animateY() + .animateZ() }; static int[] LENGTHS = new int[] { // ANIMATION_TYPE_ADD - StackStateAnimator.ANIMATION_DURATION_STANDARD, + StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR, // ANIMATION_TYPE_REMOVE - StackStateAnimator.ANIMATION_DURATION_STANDARD, + StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR, // ANIMATION_TYPE_REMOVE_SWIPED_OUT StackStateAnimator.ANIMATION_DURATION_STANDARD, @@ -1621,22 +1680,27 @@ public class NotificationStackScrollLayout extends ViewGroup // ANIMATION_TYPE_DIMMED StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED, + + // ANIMATION_TYPE_CHANGE_POSITION + StackStateAnimator.ANIMATION_DURATION_STANDARD, }; - static int ANIMATION_TYPE_ADD = 0; - static int ANIMATION_TYPE_REMOVE = 1; - static int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 2; - static int ANIMATION_TYPE_TOP_PADDING_CHANGED = 3; - static int ANIMATION_TYPE_START_DRAG = 4; - static int ANIMATION_TYPE_SNAP_BACK = 5; - static int ANIMATION_TYPE_ACTIVATED_CHILD = 6; - static int ANIMATION_TYPE_DIMMED = 7; + static final int ANIMATION_TYPE_ADD = 0; + static final int ANIMATION_TYPE_REMOVE = 1; + static final int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 2; + static final int ANIMATION_TYPE_TOP_PADDING_CHANGED = 3; + static final int ANIMATION_TYPE_START_DRAG = 4; + static final int ANIMATION_TYPE_SNAP_BACK = 5; + static final int ANIMATION_TYPE_ACTIVATED_CHILD = 6; + static final int ANIMATION_TYPE_DIMMED = 7; + static final int ANIMATION_TYPE_CHANGE_POSITION = 8; final long eventStartTime; final View changingView; final int animationType; final AnimationFilter filter; final long length; + View viewAfterChangingView; AnimationEvent(View view, int type) { eventStartTime = AnimationUtils.currentAnimationTimeMillis(); 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 d572ea5..bd2541a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java @@ -208,6 +208,8 @@ public class StackScrollAlgorithm { for (int i = 0; i < childCount; i++) { ExpandableView v = (ExpandableView) hostView.getChildAt(i); if (v.getVisibility() != View.GONE) { + StackScrollState.ViewState viewState = resultState.getViewStateForView(v); + viewState.notGoneIndex = state.visibleChildren.size(); state.visibleChildren.add(v); } } 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 011411c..44e10be 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java @@ -37,15 +37,14 @@ 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(); - private int mBackgroundRoundedRectCornerRadius; - private final Outline mChildOutline = new Outline(); public StackScrollState(ViewGroup hostView) { mHostView = hostView; mStateMap = new HashMap<ExpandableView, ViewState>(); - mBackgroundRoundedRectCornerRadius = hostView.getResources().getDimensionPixelSize( + mRoundedRectCornerRadius = mHostView.getResources().getDimensionPixelSize( com.android.internal.R.dimen.notification_quantum_rounded_rect_radius); } @@ -66,6 +65,7 @@ public class StackScrollState { viewState.height = child.getIntrinsicHeight(); viewState.gone = child.getVisibility() == View.GONE; viewState.alpha = 1; + viewState.notGoneIndex = -1; } } @@ -158,11 +158,15 @@ public class StackScrollState { // 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); + : newNotificationEnd - (previousNotificationEnd - clippingCorrection); updateChildClippingAndBackground(child, newHeight, clipHeight, @@ -190,7 +194,7 @@ public class StackScrollState { if (nextChild != null) { ViewState nextState = getViewStateForView(nextChild); boolean startIsAboveNext = nextState.yTranslation > speedBumpStart; - speedBump.animateDivider(startIsAboveNext); + speedBump.animateDivider(startIsAboveNext, null /* onFinishedRunnable */); // handle expanded case if (speedBump.isExpanded()) { @@ -272,6 +276,11 @@ public class StackScrollState { boolean dimmed; /** + * The index of the view, only accounting for views not equal to GONE + */ + int notGoneIndex; + + /** * The location this view is currently rendered at. * * <p>See <code>LOCATION_</code> flags.</p> 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 a9dcdd6..f019e6c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java @@ -39,7 +39,11 @@ import java.util.Stack; public class StackStateAnimator { public static final int ANIMATION_DURATION_STANDARD = 360; + public static final int ANIMATION_DURATION_APPEAR_DISAPPEAR = 464; public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220; + public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80; + public static final int ANIMATION_DELAY_PER_ELEMENT_MANUAL = 32; + private static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2; private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag; private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag; @@ -62,10 +66,9 @@ public class StackStateAnimator { private final Interpolator mFastOutSlowInInterpolator; public NotificationStackScrollLayout mHostLayout; - private ArrayList<NotificationStackScrollLayout.AnimationEvent> mHandledEvents = - new ArrayList<>(); private ArrayList<NotificationStackScrollLayout.AnimationEvent> mNewEvents = new ArrayList<>(); + private ArrayList<View> mNewAddChildren = new ArrayList<>(); private Set<Animator> mAnimatorSet = new HashSet<Animator>(); private Stack<AnimatorListenerAdapter> mAnimationListenerPool = new Stack<AnimatorListenerAdapter>(); @@ -96,57 +99,130 @@ public class StackStateAnimator { mCurrentLength = NotificationStackScrollLayout.AnimationEvent.combineLength(mNewEvents); for (int i = 0; i < childCount; i++) { final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i); + StackScrollState.ViewState viewState = finalState.getViewStateForView(child); - if (viewState == null) { + if (viewState == null || child.getVisibility() == View.GONE) { continue; } - startAnimations(child, viewState); - child.setClipBounds(null); + startAnimations(child, viewState, finalState); } if (!isRunning()) { // no child has preformed any animation, lets finish onAnimationFinished(); } + mNewEvents.clear(); + mNewAddChildren.clear(); } /** * Start an animation to the given viewState */ - private void startAnimations(final ExpandableView child, StackScrollState.ViewState viewState) { + private void startAnimations(final ExpandableView child, StackScrollState.ViewState viewState, + StackScrollState finalState) { int childVisibility = child.getVisibility(); boolean wasVisible = childVisibility == View.VISIBLE; final float alpha = viewState.alpha; if (!wasVisible && alpha != 0 && !viewState.gone) { child.setVisibility(View.VISIBLE); } + + boolean yTranslationChanging = child.getTranslationY() != viewState.yTranslation; + boolean zTranslationChanging = child.getTranslationZ() != viewState.zTranslation; + boolean scaleChanging = child.getScaleX() != viewState.scale; + boolean alphaChanging = alpha != child.getAlpha(); + boolean heightChanging = viewState.height != child.getActualHeight(); + boolean wasAdded = mNewAddChildren.contains(child); + boolean hasDelays = mAnimationFilter.hasDelays; + boolean isDelayRelevant = yTranslationChanging || zTranslationChanging || scaleChanging || + alphaChanging || heightChanging; + long delay = 0; + if (hasDelays && isDelayRelevant || wasAdded) { + delay = calculateChildAnimationDelay(viewState, finalState); + } + // start translationY animation - if (child.getTranslationY() != viewState.yTranslation) { - startYTranslationAnimation(child, viewState); + if (yTranslationChanging) { + startYTranslationAnimation(child, viewState, delay); } + // start translationZ animation - if (child.getTranslationZ() != viewState.zTranslation) { - startZTranslationAnimation(child, viewState); + if (zTranslationChanging) { + startZTranslationAnimation(child, viewState, delay); } + // start scale animation - if (child.getScaleX() != viewState.scale) { + if (scaleChanging) { startScaleAnimation(child, viewState); } + // start alpha animation - if (alpha != child.getAlpha()) { - startAlphaAnimation(child, viewState); + if (alphaChanging) { + startAlphaAnimation(child, viewState, delay); } + // start height animation - if (viewState.height != child.getActualHeight()) { - startHeightAnimation(child, viewState); + if (heightChanging) { + startHeightAnimation(child, viewState, delay); } + // start dimmed animation child.setDimmed(viewState.dimmed, mAnimationFilter.animateDimmed); + + if (wasAdded) { + child.performAddAnimation(delay); + } + } + + private long calculateChildAnimationDelay(StackScrollState.ViewState viewState, + StackScrollState finalState) { + long minDelay = 0; + for (NotificationStackScrollLayout.AnimationEvent event : mNewEvents) { + long delayPerElement = ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING; + switch (event.animationType) { + case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD: { + int ownIndex = viewState.notGoneIndex; + int changingIndex = finalState + .getViewStateForView(event.changingView).notGoneIndex; + int difference = Math.abs(ownIndex - changingIndex); + difference = Math.max(0, Math.min(DELAY_EFFECT_MAX_INDEX_DIFFERENCE, + difference - 1)); + long delay = (DELAY_EFFECT_MAX_INDEX_DIFFERENCE - difference) * delayPerElement; + minDelay = Math.max(delay, minDelay); + break; + } + case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT: + delayPerElement = ANIMATION_DELAY_PER_ELEMENT_MANUAL; + case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE: { + int ownIndex = viewState.notGoneIndex; + boolean noNextView = event.viewAfterChangingView == null; + View viewAfterChangingView = noNextView + ? mHostLayout.getLastChildNotGone() + : event.viewAfterChangingView; + + int nextIndex = finalState + .getViewStateForView(viewAfterChangingView).notGoneIndex; + if (ownIndex >= nextIndex) { + // we only have the view afterwards + ownIndex++; + } + int difference = Math.abs(ownIndex - nextIndex); + difference = Math.max(0, Math.min(DELAY_EFFECT_MAX_INDEX_DIFFERENCE, + difference - 1)); + long delay = difference * delayPerElement; + minDelay = Math.max(delay, minDelay); + break; + } + default: + break; + } + } + return minDelay; } private void startHeightAnimation(final ExpandableView child, - StackScrollState.ViewState viewState) { + StackScrollState.ViewState viewState, long delay) { Integer previousStartValue = getChildTag(child, TAG_START_HEIGHT); Integer previousEndValue = getChildTag(child, TAG_END_HEIGHT); int newEndValue = viewState.height; @@ -185,6 +261,9 @@ public class StackStateAnimator { 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() { @@ -195,14 +274,14 @@ public class StackStateAnimator { child.setTag(TAG_END_HEIGHT, null); } }); - startInstantly(animator); + startAnimator(animator); child.setTag(TAG_ANIMATOR_HEIGHT, animator); child.setTag(TAG_START_HEIGHT, child.getActualHeight()); child.setTag(TAG_END_HEIGHT, newEndValue); } private void startAlphaAnimation(final ExpandableView child, - final StackScrollState.ViewState viewState) { + final StackScrollState.ViewState viewState, long delay) { Float previousStartValue = getChildTag(child,TAG_START_ALPHA); Float previousEndValue = getChildTag(child,TAG_END_ALPHA); final float newEndValue = viewState.alpha; @@ -236,14 +315,13 @@ public class StackStateAnimator { child.getAlpha(), newEndValue); animator.setInterpolator(mFastOutSlowInInterpolator); // Handle layer type - final int currentLayerType = child.getLayerType(); child.setLayerType(View.LAYER_TYPE_HARDWARE, null); animator.addListener(new AnimatorListenerAdapter() { public boolean mWasCancelled; @Override public void onAnimationEnd(Animator animation) { - child.setLayerType(currentLayerType, null); + child.setLayerType(View.LAYER_TYPE_NONE, null); if (newEndValue == 0 && !mWasCancelled) { child.setVisibility(View.INVISIBLE); } @@ -264,6 +342,9 @@ public class StackStateAnimator { }); 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() { @@ -272,14 +353,14 @@ public class StackStateAnimator { } }); - startInstantly(animator); + startAnimator(animator); child.setTag(TAG_ANIMATOR_ALPHA, animator); child.setTag(TAG_START_ALPHA, child.getAlpha()); child.setTag(TAG_END_ALPHA, newEndValue); } private void startZTranslationAnimation(final ExpandableView child, - final StackScrollState.ViewState viewState) { + final StackScrollState.ViewState viewState, long delay) { Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Z); Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Z); float newEndValue = viewState.zTranslation; @@ -311,6 +392,9 @@ public class StackStateAnimator { 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() { @@ -321,14 +405,14 @@ public class StackStateAnimator { child.setTag(TAG_END_TRANSLATION_Z, null); } }); - startInstantly(animator); + startAnimator(animator); child.setTag(TAG_ANIMATOR_TRANSLATION_Z, animator); child.setTag(TAG_START_TRANSLATION_Z, child.getTranslationZ()); child.setTag(TAG_END_TRANSLATION_Z, newEndValue); } private void startYTranslationAnimation(final ExpandableView child, - StackScrollState.ViewState viewState) { + StackScrollState.ViewState viewState, long delay) { Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Y); Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Y); float newEndValue = viewState.yTranslation; @@ -361,6 +445,9 @@ public class StackStateAnimator { 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() { @@ -371,7 +458,7 @@ public class StackStateAnimator { child.setTag(TAG_END_TRANSLATION_Y, null); } }); - startInstantly(animator); + startAnimator(animator); child.setTag(TAG_ANIMATOR_TRANSLATION_Y, animator); child.setTag(TAG_START_TRANSLATION_Y, child.getTranslationY()); child.setTag(TAG_END_TRANSLATION_Y, newEndValue); @@ -425,18 +512,15 @@ public class StackStateAnimator { child.setTag(TAG_END_SCALE, null); } }); - startInstantly(animator); + startAnimator(animator); child.setTag(TAG_ANIMATOR_SCALE, animator); child.setTag(TAG_START_SCALE, child.getScaleX()); child.setTag(TAG_END_SCALE, newEndValue); } - /** - * Start an animator instantly instead of waiting on the next synchronization frame - */ - public static void startInstantly(ValueAnimator animator) { + private void startAnimator(ValueAnimator animator) { + mAnimatorSet.add(animator); animator.start(); - animator.setCurrentPlayTime(0); } /** @@ -468,7 +552,6 @@ public class StackStateAnimator { @Override public void onAnimationStart(Animator animation) { - mAnimatorSet.add(animation); mWasCancelled = false; } }; @@ -497,8 +580,6 @@ public class StackStateAnimator { } private void onAnimationFinished() { - mHandledEvents.clear(); - mNewEvents.clear(); mHostLayout.onChildAnimationFinished(); } @@ -511,27 +592,60 @@ public class StackStateAnimator { private void processAnimationEvents( ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents, StackScrollState finalState) { - mNewEvents.clear(); for (NotificationStackScrollLayout.AnimationEvent event : animationEvents) { - View changingView = event.changingView; - if (!mHandledEvents.contains(event)) { - if (event.animationType == NotificationStackScrollLayout.AnimationEvent - .ANIMATION_TYPE_ADD) { - - // This item is added, initialize it's properties. - StackScrollState.ViewState viewState = finalState - .getViewStateForView(changingView); - if (viewState == null) { - // The position for this child was never generated, let's continue. - continue; - } - changingView.setAlpha(0); - changingView.setTranslationY(viewState.yTranslation); - changingView.setTranslationZ(viewState.zTranslation); + final ExpandableView changingView = (ExpandableView) event.changingView; + if (event.animationType == + NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD) { + + // This item is added, initialize it's properties. + StackScrollState.ViewState viewState = finalState + .getViewStateForView(changingView); + if (viewState == null) { + // The position for this child was never generated, let's continue. + continue; + } + if (changingView.getVisibility() == View.GONE) { + // The view was set to gone but the state never removed + finalState.removeViewStateForView(changingView); + continue; } - mHandledEvents.add(event); - mNewEvents.add(event); + changingView.setAlpha(viewState.alpha); + changingView.setTranslationY(viewState.yTranslation); + changingView.setTranslationZ(viewState.zTranslation); + changingView.setActualHeight(viewState.height, false); + mNewAddChildren.add(changingView); + + } else if (event.animationType == + NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE) { + if (changingView.getVisibility() == View.GONE) { + continue; + } + + // Find the amount to translate up. This is needed in order to understand the + // direction of the remove animation (either downwards or upwards) + StackScrollState.ViewState viewState = finalState + .getViewStateForView(event.viewAfterChangingView); + int actualHeight = changingView.getActualHeight(); + // upwards by default + float translationDirection = -1.0f; + if (viewState != null) { + // there was a view after this one, Approximate the distance the next child + // travelled + translationDirection = ((viewState.yTranslation + - (changingView.getTranslationY() + actualHeight / 2.0f)) * 2 / + actualHeight); + translationDirection = Math.max(Math.min(translationDirection, 1.0f),-1.0f); + + } + changingView.performRemoveAnimation(translationDirection, new Runnable() { + @Override + public void run() { + // remove the temporary overlay + mHostLayout.getOverlay().remove(changingView); + } + }); } + mNewEvents.add(event); } } 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 9006c9a..25147b4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java @@ -46,15 +46,23 @@ public class TvStatusBar extends BaseStatusBar { } @Override - public void addNotification(IBinder key, StatusBarNotification notification) { + public void addNotification(StatusBarNotification notification) { } @Override - public void updateNotification(IBinder key, StatusBarNotification notification) { + public void addNotificationInternal(StatusBarNotification notification) { } @Override - public void removeNotification(IBinder key) { + public void updateNotification(StatusBarNotification notification) { + } + + @Override + protected void removeNotificationInternal(String key) { + } + + @Override + public void removeNotification(String key) { } @Override @@ -113,7 +121,7 @@ public class TvStatusBar extends BaseStatusBar { } @Override - protected void tick(IBinder key, StatusBarNotification n, boolean firstTime) { + protected void tick(StatusBarNotification n, boolean firstTime) { } @Override |