diff --git a/packages/SystemUI/res/layout/status_bar_notification_shelf.xml b/packages/SystemUI/res/layout/status_bar_notification_shelf.xml index fde61ebc77b9a..088deba807f55 100644 --- a/packages/SystemUI/res/layout/status_bar_notification_shelf.xml +++ b/packages/SystemUI/res/layout/status_bar_notification_shelf.xml @@ -35,8 +35,8 @@ android:id="@+id/content" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_marginStart="13dp" - android:layout_marginEnd="13dp" + android:paddingStart="13dp" + android:paddingEnd="13dp" android:gravity="center_vertical" /> 6dp + + 1dp + + + 3dp + 20dp diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 9a1e0649c805f..de88e6650245f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -48,7 +48,7 @@ public class NotificationShelf extends ActivatableNotificationView { private ViewInvertHelper mViewInvertHelper; private boolean mDark; - private NotificationIconContainer mNotificationIconContainer; + private NotificationIconContainer mShelfIcons; private ArrayList mIcons = new ArrayList<>(); private ShelfState mShelfState; private int[] mTmp = new int[2]; @@ -62,6 +62,7 @@ public class NotificationShelf extends ActivatableNotificationView { private int mPaddingBetweenElements; private int mNotGoneIndex; private boolean mHasItemsInStableShelf; + private NotificationIconContainer mCollapsedIcons; public NotificationShelf(Context context, AttributeSet attrs) { super(context, attrs); @@ -70,14 +71,15 @@ public class NotificationShelf extends ActivatableNotificationView { @Override protected void onFinishInflate() { super.onFinishInflate(); - mNotificationIconContainer = (NotificationIconContainer) findViewById(R.id.content); - mNotificationIconContainer.setClipChildren(false); - mNotificationIconContainer.setClipToPadding(false); + mShelfIcons = (NotificationIconContainer) findViewById(R.id.content); + mShelfIcons.setClipChildren(false); + mShelfIcons.setClipToPadding(false); + setClipToActualHeight(false); setClipChildren(false); setClipToPadding(false); - mNotificationIconContainer.setShowAllIcons(false); - mViewInvertHelper = new ViewInvertHelper(mNotificationIconContainer, + mShelfIcons.setShowAllIcons(false); + mViewInvertHelper = new ViewInvertHelper(mShelfIcons, NotificationPanelView.DOZE_ANIMATION_DURATION); mShelfState = new ShelfState(); initDimens(); @@ -118,11 +120,11 @@ public class NotificationShelf extends ActivatableNotificationView { @Override protected View getContentView() { - return mNotificationIconContainer; + return mShelfIcons; } - public NotificationIconContainer getNotificationIconContainer() { - return mNotificationIconContainer; + public NotificationIconContainer getShelfIcons() { + return mShelfIcons; } @Override @@ -142,13 +144,13 @@ public class NotificationShelf extends ActivatableNotificationView { float viewEnd = lastViewState.yTranslation + lastViewState.height; mShelfState.copyFrom(lastViewState); mShelfState.height = getIntrinsicHeight(); - mShelfState.yTranslation = Math.min(viewEnd, maxShelfEnd) - mShelfState.height; + mShelfState.yTranslation = Math.max(Math.min(viewEnd, maxShelfEnd) - mShelfState.height, + getFullyClosedTranslation()); mShelfState.zTranslation = ambientState.getBaseZHeight(); float openedAmount = (mShelfState.yTranslation - getFullyClosedTranslation()) / (getIntrinsicHeight() * 2); openedAmount = Math.min(1.0f, openedAmount); - mShelfState.iconContainerTranslation = (1.0f - openedAmount) - * (mStatusBarPaddingStart - mNotificationIconContainer.getLeft()); + mShelfState.openedAmount = openedAmount; mShelfState.clipTopAmount = 0; mShelfState.alpha = 1.0f; mShelfState.belowShelf = false; @@ -172,10 +174,16 @@ public class NotificationShelf extends ActivatableNotificationView { */ public void updateAppearance() { WeakHashMap iconStates = - mNotificationIconContainer.resetViewStates(); + mShelfIcons.resetViewStates(); float numIconsInShelf = 0.0f; int shelfIndex = mAmbientState.getShelfIndex(); mNotGoneIndex = -1; + float interpolationStart = mMaxLayoutHeight - getIntrinsicHeight() * 2; + float expandAmount = 0.0f; + if (getTranslationY() >= interpolationStart) { + expandAmount = (getTranslationY() - interpolationStart) / getIntrinsicHeight(); + expandAmount = Math.min(1.0f, expandAmount); + } // find the first view that doesn't overlap with the shelf int notificationIndex = 0; int notGoneNotifications = 0; @@ -205,7 +213,7 @@ public class NotificationShelf extends ActivatableNotificationView { } } updateNotificationClipHeight(row, notificationClipEnd); - updateIconAppearance(row, iconState, icon); + updateIconAppearance(row, iconState, icon, expandAmount); numIconsInShelf += iconState.iconAppearAmount; if (row.getTranslationY() >= getTranslationY() && mNotGoneIndex == -1) { mNotGoneIndex = notGoneNotifications; @@ -228,8 +236,8 @@ public class NotificationShelf extends ActivatableNotificationView { } notificationIndex++; } - mNotificationIconContainer.calculateIconTranslations(); - mNotificationIconContainer.applyIconStates(); + mShelfIcons.calculateIconTranslations(); + mShelfIcons.applyIconStates(); setVisibility(numIconsInShelf == 0.0f || !mAmbientState.isShadeExpanded() ? INVISIBLE : VISIBLE); setHideBackground(numIconsInShelf < 1.0f); @@ -247,8 +255,8 @@ public class NotificationShelf extends ActivatableNotificationView { } } - private void updateIconAppearance(ExpandableNotificationRow row, - IconState iconState, StatusBarIconView icon) { + private void updateIconAppearance(ExpandableNotificationRow row, IconState iconState, + StatusBarIconView icon, float expandAmount) { // Let calculate how much the view is in the shelf float viewStart = row.getTranslationY(); int transformHeight = row.getActualHeight() + mPaddingBetweenElements; @@ -259,12 +267,6 @@ public class NotificationShelf extends ActivatableNotificationView { float linearAmount = (getTranslationY() - viewStart) / transformHeight; float interpolatedAmount = Interpolators.ACCELERATE_DECELERATE.getInterpolation( linearAmount); - float interpolationStart = mMaxLayoutHeight - getIntrinsicHeight() * 2; - float expandAmount = 0.0f; - if (getTranslationY() >= interpolationStart) { - expandAmount = (getTranslationY() - interpolationStart) / getIntrinsicHeight(); - expandAmount = Math.min(1.0f, expandAmount); - } interpolatedAmount = NotificationUtils.interpolate( interpolatedAmount, linearAmount, expandAmount); iconState.iconAppearAmount = 1.0f - interpolatedAmount; @@ -318,7 +320,7 @@ public class NotificationShelf extends ActivatableNotificationView { // The notification size is different from the size in the shelf / statusbar float newSize = NotificationUtils.interpolate(notificationIconSize, shelfIconSize, transitionAmount); - iconState.scaleX = newSize / icon.getHeight(); + iconState.scaleX = newSize / icon.getHeight() / icon.getIconScale(); iconState.scaleY = iconState.scaleX; iconState.hidden = transitionAmount == 0.0f; row.setIconTransformationAmount(transitionAmount); @@ -326,8 +328,8 @@ public class NotificationShelf extends ActivatableNotificationView { if (row.isInShelf() && !row.isTransformingIntoShelf()) { iconState.iconAppearAmount = 1.0f; iconState.alpha = 1.0f; - iconState.scaleX = shelfIconSize / icon.getHeight(); - iconState.scaleY = iconState.scaleX; + iconState.scaleX = 1.0f; + iconState.scaleY = 1.0f; iconState.hidden = false; } } @@ -378,8 +380,23 @@ public class NotificationShelf extends ActivatableNotificationView { return super.shouldHideBackground() || mHideBackground; } - private void setIconContainerTranslation(float iconContainerTranslation) { - mNotificationIconContainer.setTranslationX(iconContainerTranslation); + private void setOpenedAmount(float openedAmount) { + mCollapsedIcons.getLocationOnScreen(mTmp); + int start = mTmp[0]; + if (isLayoutRtl()) { + start = getWidth() - start - mCollapsedIcons.getWidth(); + } + int width = (int) NotificationUtils.interpolate(start + mCollapsedIcons.getWidth(), + mShelfIcons.getWidth(), + openedAmount); + mShelfIcons.setActualLayoutWidth(width); + float padding = NotificationUtils.interpolate(mCollapsedIcons.getPaddingEnd(), + mShelfIcons.getPaddingEnd(), + openedAmount); + mShelfIcons.setActualPaddingEnd(padding); + float paddingStart = NotificationUtils.interpolate(start, + mShelfIcons.getPaddingStart(), openedAmount); + mShelfIcons.setActualPaddingStart(paddingStart); } public void setMaxLayoutHeight(int maxLayoutHeight) { @@ -405,23 +422,27 @@ public class NotificationShelf extends ActivatableNotificationView { return mHasItemsInStableShelf; } + public void setCollapsedIcons(NotificationIconContainer collapsedIcons) { + mCollapsedIcons = collapsedIcons; + } + private class ShelfState extends ExpandableViewState { - private float iconContainerTranslation; + private float openedAmount; private boolean hasItemsInStableShelf; @Override public void applyToView(View view) { super.applyToView(view); updateAppearance(); - setIconContainerTranslation(iconContainerTranslation); + setOpenedAmount(openedAmount); setHasItemsInStableShelf(hasItemsInStableShelf); } @Override public void animateTo(View child, AnimationProperties properties) { super.animateTo(child, properties); + setOpenedAmount(openedAmount); updateAppearance(); - setIconContainerTranslation(iconContainerTranslation); setHasItemsInStableShelf(hasItemsInStableShelf); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index 03e366283b4ed..543f899283d4b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -16,6 +16,10 @@ package com.android.systemui.statusbar; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; import android.app.Notification; import android.content.Context; import android.content.pm.ApplicationInfo; @@ -30,20 +34,55 @@ import android.os.Parcelable; import android.os.UserHandle; import android.text.TextUtils; import android.util.AttributeSet; +import android.util.FloatProperty; import android.util.Log; +import android.util.Property; import android.util.TypedValue; import android.view.ViewDebug; import android.view.accessibility.AccessibilityEvent; +import android.view.animation.Interpolator; import com.android.internal.statusbar.StatusBarIcon; +import com.android.systemui.Interpolators; import com.android.systemui.R; +import com.android.systemui.statusbar.notification.NotificationUtils; import java.text.NumberFormat; public class StatusBarIconView extends AnimatedImageView { - private static final String TAG = "StatusBarIconView"; - private boolean mAlwaysScaleIcon; + public static final int STATE_ICON = 0; + public static final int STATE_DOT = 1; + public static final int STATE_HIDDEN = 2; + private static final String TAG = "StatusBarIconView"; + private static final Property ICON_APPEAR_AMOUNT + = new FloatProperty("iconAppearAmount") { + + @Override + public void setValue(StatusBarIconView object, float value) { + object.setIconAppearAmount(value); + } + + @Override + public Float get(StatusBarIconView object) { + return object.getIconAppearAmount(); + } + }; + private static final Property DOT_APPEAR_AMOUNG + = new FloatProperty("dot_appear_amount") { + + @Override + public void setValue(StatusBarIconView object, float value) { + object.setDotAppearAmount(value); + } + + @Override + public Float get(StatusBarIconView object) { + return object.getDotAppearAmount(); + } + }; + + private boolean mAlwaysScaleIcon; private StatusBarIcon mIcon; @ViewDebug.ExportedProperty private String mSlot; private Drawable mNumberBackground; @@ -55,6 +94,15 @@ public class StatusBarIconView extends AnimatedImageView { private final boolean mBlocked; private int mDensity; private float mIconScale = 1.0f; + private final Paint mDotPaint = new Paint(); + private boolean mDotVisible; + private float mDotRadius; + private int mStaticDotRadius; + private int mVisibleState = STATE_ICON; + private float mIconAppearAmount = 1.0f; + private ObjectAnimator mIconAppearAnimator; + private ObjectAnimator mDotAnimator; + private float mDotAppearAmount; public StatusBarIconView(Context context, String slot, Notification notification) { this(context, slot, notification, false); @@ -73,6 +121,11 @@ public class StatusBarIconView extends AnimatedImageView { maybeUpdateIconScale(); setScaleType(ScaleType.CENTER); mDensity = context.getResources().getDisplayMetrics().densityDpi; + if (mNotification != null) { + setIconTint(getContext().getColor( + com.android.internal.R.color.notification_icon_default_color)); + } + reloadDimens(); } private void maybeUpdateIconScale() { @@ -88,8 +141,6 @@ public class StatusBarIconView extends AnimatedImageView { final int outerBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_size); final int imageBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size); mIconScale = (float)imageBounds / (float)outerBounds; - setScaleX(mIconScale); - setScaleY(mIconScale); } public float getIconScale() { @@ -104,6 +155,15 @@ public class StatusBarIconView extends AnimatedImageView { mDensity = density; maybeUpdateIconScale(); updateDrawable(); + reloadDimens(); + } + } + + private void reloadDimens() { + boolean applyRadius = mDotRadius == mStaticDotRadius; + mStaticDotRadius = getResources().getDimensionPixelSize(R.dimen.overflow_dot_radius); + if (applyRadius) { + mDotRadius = mStaticDotRadius; } } @@ -264,12 +324,32 @@ public class StatusBarIconView extends AnimatedImageView { @Override protected void onDraw(Canvas canvas) { - super.onDraw(canvas); + if (mIconAppearAmount > 0.0f) { + canvas.save(); + canvas.scale(mIconScale * mIconAppearAmount, mIconScale * mIconAppearAmount, + getWidth() / 2, getHeight() / 2); + super.onDraw(canvas); + canvas.restore(); + } if (mNumberBackground != null) { mNumberBackground.draw(canvas); canvas.drawText(mNumberText, mNumberX, mNumberY, mNumberPain); } + if (mDotAppearAmount != 0.0f) { + float radius; + float alpha; + if (mDotAppearAmount <= 1.0f) { + radius = mDotRadius * mDotAppearAmount; + alpha = 1.0f; + } else { + float fadeOutAmount = mDotAppearAmount - 1.0f; + alpha = 1.0f - fadeOutAmount; + radius = NotificationUtils.interpolate(mDotRadius, getWidth() / 4, fadeOutAmount); + } + mDotPaint.setAlpha((int) (alpha * 255)); + canvas.drawCircle(getWidth() / 2, getHeight() / 2, radius, mDotPaint); + } } @Override @@ -356,4 +436,76 @@ public class StatusBarIconView extends AnimatedImageView { return c.getString(R.string.accessibility_desc_notification_icon, appName, desc); } + public void setIconTint(int iconTint) { + mDotPaint.setColor(iconTint); + } + + public void setVisibleState(int visibleState) { + if (visibleState != mVisibleState) { + mVisibleState = visibleState; + if (mIconAppearAnimator != null) { + mIconAppearAnimator.cancel(); + } + float targetAmount = 0.0f; + Interpolator interpolator = Interpolators.FAST_OUT_LINEAR_IN; + if (visibleState == STATE_ICON) { + targetAmount = 1.0f; + interpolator = Interpolators.LINEAR_OUT_SLOW_IN; + } + mIconAppearAnimator = ObjectAnimator.ofFloat(this, ICON_APPEAR_AMOUNT, + targetAmount); + mIconAppearAnimator.setInterpolator(interpolator); + mIconAppearAnimator.setDuration(100); + mIconAppearAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mIconAppearAnimator = null; + } + }); + mIconAppearAnimator.start(); + + if (mDotAnimator != null) { + mDotAnimator.cancel(); + } + targetAmount = visibleState == STATE_ICON ? 2.0f : 0.0f; + interpolator = Interpolators.FAST_OUT_LINEAR_IN; + if (visibleState == STATE_DOT) { + targetAmount = 1.0f; + interpolator = Interpolators.LINEAR_OUT_SLOW_IN; + } + mDotAnimator = ObjectAnimator.ofFloat(this, DOT_APPEAR_AMOUNG, + targetAmount); + mDotAnimator.setInterpolator(interpolator); + mDotAnimator.setDuration(100); + mDotAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mDotAnimator = null; + } + }); + mDotAnimator.start(); + } + } + + public void setIconAppearAmount(Float iconAppearAmount) { + mIconAppearAmount = iconAppearAmount; + invalidate(); + } + + public float getIconAppearAmount() { + return mIconAppearAmount; + } + + public int getVisibleState() { + return mVisibleState; + } + + public void setDotAppearAmount(float dotAppearAmount) { + mDotAppearAmount = dotAppearAmount; + invalidate(); + } + + public float getDotAppearAmount() { + return mDotAppearAmount; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java index 72e84a464fd28..7b67f31b7a4a5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java @@ -8,7 +8,6 @@ import android.graphics.Rect; import android.support.annotation.NonNull; import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; import android.widget.LinearLayout; import com.android.internal.util.NotificationColorUtil; @@ -36,7 +35,6 @@ public class NotificationIconAreaController { private PhoneStatusBar mPhoneStatusBar; protected View mNotificationIconArea; - private NotificationShelf mNotificationIconAreaScroller; private NotificationIconContainer mNotificationIcons; private NotificationIconContainer mNotificationIconsScroller; private final Rect mTintArea = new Rect(); @@ -66,8 +64,9 @@ public class NotificationIconAreaController { mNotificationIcons = (NotificationIconContainer) mNotificationIconArea.findViewById( R.id.notificationIcons); - mNotificationIconAreaScroller = mPhoneStatusBar.getNotificationShelf(); - mNotificationIconsScroller = mNotificationIconAreaScroller.getNotificationIconContainer(); + NotificationShelf shelf = mPhoneStatusBar.getNotificationShelf(); + mNotificationIconsScroller = shelf.getShelfIcons(); + shelf.setCollapsedIcons(mNotificationIcons); mNotificationScrollLayout = mPhoneStatusBar.getNotificationScrollLayout(); } @@ -115,12 +114,10 @@ public class NotificationIconAreaController { } /** - * Sets the color that should be used to tint any icons in the notification area. If this - * method is not called, the default tint is {@link Color#WHITE}. + * Sets the color that should be used to tint any icons in the notification area. */ public void setIconTint(int iconTint) { mIconTint = iconTint; - mNotificationIcons.setIconTint(mIconTint); applyNotificationIconsTint(); } @@ -178,7 +175,7 @@ public class NotificationIconAreaController { */ private void updateIconsForLayout(NotificationData notificationData, Function function, - ViewGroup hostLayout) { + NotificationIconContainer hostLayout) { ArrayList toShow = new ArrayList<>( mNotificationScrollLayout.getChildCount()); @@ -193,6 +190,7 @@ public class NotificationIconAreaController { } } + ArrayList toRemove = new ArrayList<>(); for (int i = 0; i < hostLayout.getChildCount(); i++) { View child = hostLayout.getChildAt(i); @@ -239,6 +237,7 @@ public class NotificationIconAreaController { v.setImageTintList(ColorStateList.valueOf( StatusBarIconController.getTint(mTintArea, v, mIconTint))); } + v.setIconTint(mIconTint); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java index 11563d5945cd6..0f41e31d9b366 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -17,10 +17,18 @@ package com.android.systemui.statusbar.phone; import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; import android.util.AttributeSet; import android.view.View; +import com.android.systemui.R; import com.android.systemui.statusbar.AlphaOptimizedFrameLayout; +import com.android.systemui.statusbar.StatusBarIconView; +import com.android.systemui.statusbar.stack.AnimationFilter; +import com.android.systemui.statusbar.stack.AnimationProperties; import com.android.systemui.statusbar.stack.ViewState; import java.util.WeakHashMap; @@ -31,15 +39,48 @@ import java.util.WeakHashMap; */ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { private static final String TAG = "NotificationIconContainer"; + private static final boolean DEBUG = false; + private static final AnimationFilter DOT_ANIMATION_FILTER = new AnimationFilter().animateX(); + private static final AnimationProperties DOT_ANIMATION_PROPERTIES = new AnimationProperties() { + @Override + public AnimationFilter getAnimationFilter() { + return DOT_ANIMATION_FILTER; + } + }.setDuration(200); private boolean mShowAllIcons = true; - private int mIconTint; private WeakHashMap mIconStates = new WeakHashMap<>(); + private int mDotPadding; + private int mStaticDotRadius; + private int mActualLayoutWidth = -1; + private float mActualPaddingEnd = -1; + private float mActualPaddingStart = -1; public NotificationIconContainer(Context context, AttributeSet attrs) { super(context, attrs); + initDimens(); + setWillNotDraw(!DEBUG); } + private void initDimens() { + mDotPadding = getResources().getDimensionPixelSize(R.dimen.overflow_icon_dot_padding); + mStaticDotRadius = getResources().getDimensionPixelSize(R.dimen.overflow_dot_radius); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + Paint paint = new Paint(); + paint.setColor(Color.RED); + paint.setStyle(Paint.Style.STROKE); + canvas.drawRect(getActualPaddingStart(), 0, getLayoutEnd(), getHeight(), paint); + } + + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + initDimens(); + } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { float centerY = getHeight() / 2.0f; @@ -82,10 +123,6 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { mIconStates.remove(child); } - public void setIconTint(int iconTint) { - mIconTint = iconTint; - } - public WeakHashMap resetViewStates() { for (int i = 0; i < getChildCount(); i++) { View view = getChildAt(i); @@ -100,17 +137,75 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { * are inserted into the notification container. * If this is not a whole number, the fraction means by how much the icon is appearing. */ - public WeakHashMap calculateIconTranslations() { + public void calculateIconTranslations() { + float translationX = getActualPaddingStart(); + int overflowingIconIndex = -1; + int lastTwoIconWidth = 0; int childCount = getChildCount(); - float visibleIconStart = childCount - numberOfVisibleIcons; - float translationX = 0.0f; for (int i = 0; i < childCount; i++) { View view = getChildAt(i); IconState iconState = mIconStates.get(view); iconState.xTranslation = translationX; + iconState.visibleState = StatusBarIconView.STATE_ICON; translationX += iconState.iconAppearAmount * view.getWidth(); + if (translationX > getLayoutEnd()) { + // we are overflowing it with this icon + overflowingIconIndex = i - 1; + lastTwoIconWidth = view.getWidth(); + break; + } } - return mIconStates; + if (overflowingIconIndex != -1) { + int numDots = 1; + View overflowIcon = getChildAt(overflowingIconIndex); + IconState overflowState = mIconStates.get(overflowIcon); + lastTwoIconWidth += overflowIcon.getWidth(); + int dotWidth = mStaticDotRadius * 2 + mDotPadding; + int totalDotLength = mStaticDotRadius * 6 + 2 * mDotPadding; + translationX = (getLayoutEnd() - lastTwoIconWidth / 2 - totalDotLength / 2) + - overflowIcon.getWidth() * 0.3f + mStaticDotRadius; + float overflowStart = getLayoutEnd() - lastTwoIconWidth; + float overlapAmount = (overflowState.xTranslation - overflowStart) + / overflowIcon.getWidth(); + translationX += overlapAmount * dotWidth; + for (int i = overflowingIconIndex; i < childCount; i++) { + View view = getChildAt(i); + IconState iconState = mIconStates.get(view); + iconState.xTranslation = translationX; + if (numDots <= 3) { + iconState.visibleState = StatusBarIconView.STATE_DOT; + translationX += numDots == 3 ? 3 * dotWidth : dotWidth; + } else { + iconState.visibleState = StatusBarIconView.STATE_HIDDEN; + } + numDots++; + } + } + if (isLayoutRtl()) { + for (int i = 0; i < childCount; i++) { + View view = getChildAt(i); + IconState iconState = mIconStates.get(view); + iconState.xTranslation = getWidth() - iconState.xTranslation - view.getWidth(); + } + } + } + + private float getLayoutEnd() { + return getActualWidth() - getActualPaddingEnd(); + } + + private float getActualPaddingEnd() { + if (mActualPaddingEnd < 0) { + return getPaddingEnd(); + } + return mActualPaddingEnd; + } + + private float getActualPaddingStart() { + if (mActualPaddingStart < 0) { + return getPaddingStart(); + } + return mActualPaddingStart; } /** @@ -123,12 +218,49 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { mShowAllIcons = showAllIcons; } + public void setActualLayoutWidth(int actualLayoutWidth) { + mActualLayoutWidth = actualLayoutWidth; + if (DEBUG) { + invalidate(); + } + } + + public void setActualPaddingEnd(float paddingEnd) { + mActualPaddingEnd = paddingEnd; + if (DEBUG) { + invalidate(); + } + } + + public void setActualPaddingStart(float paddingStart) { + mActualPaddingStart = paddingStart; + if (DEBUG) { + invalidate(); + } + } + + public int getActualWidth() { + if (mActualLayoutWidth < 0) { + return getWidth(); + } + return mActualLayoutWidth; + } + public static class IconState extends ViewState { public float iconAppearAmount = 1.0f; + public int visibleState; @Override public void applyToView(View view) { - super.applyToView(view); + if (view instanceof StatusBarIconView) { + StatusBarIconView icon = (StatusBarIconView) view; + if (visibleState != icon.getVisibleState()) { + icon.setVisibleState(visibleState); + animateTo(icon, DOT_ANIMATION_PROPERTIES); + } else { + super.applyToView(view); + } + } } } }