diff --git a/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml b/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml index d607c8c29eadb..c8a554467ceb5 100644 --- a/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml +++ b/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml @@ -23,6 +23,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" + android:paddingStart="2dp" android:orientation="horizontal"> + android:gravity="center_vertical" > - + + + + + - + + + + + + - + + - - - - - - - - - - - + diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 0615e2e60f39b..4c85ea9e8c082 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -56,6 +56,7 @@ import com.android.systemui.statusbar.phone.PhoneStatusBarView; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager; import com.android.systemui.statusbar.policy.Clock; +import com.android.systemui.statusbar.phone.StatusIconContainer; import com.android.systemui.statusbar.policy.DarkIconDispatcher; import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver; import com.android.systemui.statusbar.policy.DateView; @@ -147,7 +148,9 @@ public class QuickStatusBarHeader extends RelativeLayout implements mDate.setOnClickListener(this); mSystemIconsView = findViewById(R.id.quick_status_bar_system_icons); mQuickQsStatusIcons = findViewById(R.id.quick_qs_status_icons); - mIconManager = new TintedIconManager(findViewById(R.id.statusIcons)); + StatusIconContainer iconContainer = findViewById(R.id.statusIcons); + iconContainer.setShouldRestrictIcons(false); + mIconManager = new TintedIconManager(iconContainer); // Views corresponding to the header info section (e.g. tooltip and next alarm). mHeaderTextContainerView = findViewById(R.id.header_text_container); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index bd6bd12a5e9be..4fc18ad3ea54f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -163,7 +163,6 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi mNumberPain.setColor(context.getColor(R.drawable.notification_number_text_color)); mNumberPain.setAntiAlias(true); setNotification(sbn); - maybeUpdateIconScaleDimens(); setScaleType(ScaleType.CENTER); mDensity = context.getResources().getDisplayMetrics().densityDpi; if (mNotification != null) { @@ -171,26 +170,18 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi com.android.internal.R.color.notification_default_color_light)); } reloadDimens(); + maybeUpdateIconScaleDimens(); } + /** Should always be preceded by {@link #reloadDimens()} */ private void maybeUpdateIconScaleDimens() { // We do not resize and scale system icons (on the right), only notification icons (on the // left). if (mNotification != null || mAlwaysScaleIcon) { - updateIconScaleDimens(); + updateIconScale(); } } - private void updateIconScaleDimens() { - Resources res = mContext.getResources(); - mStatusBarIconSize = res.getDimensionPixelSize(R.dimen.status_bar_icon_size); - mStatusBarIconDrawingSizeDark = - res.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size_dark); - mStatusBarIconDrawingSize = - res.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size); - updateIconScale(); - } - private void updateIconScale() { final float imageBounds = NotificationUtils.interpolate( mStatusBarIconDrawingSize, @@ -214,15 +205,21 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi int density = newConfig.densityDpi; if (density != mDensity) { mDensity = density; + reloadDimens(); maybeUpdateIconScaleDimens(); updateDrawable(); - reloadDimens(); } } private void reloadDimens() { boolean applyRadius = mDotRadius == mStaticDotRadius; - mStaticDotRadius = getResources().getDimensionPixelSize(R.dimen.overflow_dot_radius); + Resources res = getResources(); + mStaticDotRadius = res.getDimensionPixelSize(R.dimen.overflow_dot_radius); + mStatusBarIconSize = res.getDimensionPixelSize(R.dimen.status_bar_icon_size); + mStatusBarIconDrawingSizeDark = + res.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size_dark); + mStatusBarIconDrawingSize = + res.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size); if (applyRadius) { mDotRadius = mStaticDotRadius; } @@ -240,7 +237,8 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi mDozer = new NotificationIconDozeHelper(context); mBlocked = false; mAlwaysScaleIcon = true; - updateIconScaleDimens(); + reloadDimens(); + updateIconScale(); mDensity = context.getResources().getDisplayMetrics().densityDpi; } @@ -425,7 +423,7 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi radius = NotificationUtils.interpolate(mDotRadius, getWidth() / 4, fadeOutAmount); } mDotPaint.setAlpha((int) (alpha * 255)); - canvas.drawCircle(getWidth() / 2, getHeight() / 2, radius, mDotPaint); + canvas.drawCircle(mStatusBarIconSize / 2, getHeight() / 2, radius, mDotPaint); } } @@ -657,6 +655,7 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi mContrastedDrawableColor = contrastedColor; } + @Override public void setVisibleState(int state) { setVisibleState(state, true /* animate */, null /* endRunnable */); } @@ -872,7 +871,10 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi @Override public void onDarkChanged(Rect area, float darkIntensity, int tint) { - setImageTintList(ColorStateList.valueOf(getTint(area, this, tint))); + int areaTint = getTint(area, this, tint); + ColorStateList color = ColorStateList.valueOf(areaTint); + setImageTintList(color); + setDecorColor(areaTint); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java index 51b42395e369a..f4e4581215b55 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java @@ -196,11 +196,21 @@ public class StatusBarMobileView extends AlphaOptimizedLinearLayout implements D mMobileRoaming.setImageTintList(list); } + @Override + public void setDecorColor(int color) { + //TODO: May also not be needed + } + @Override public boolean isIconVisible() { return mState.visible; } + @Override + public void setVisibleState(int state) { + //TODO: May not be needed. Mobile is always expected to be visible (not a dot) + } + @VisibleForTesting public MobileIconState getState() { return mState; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java index 62cd16fca3bdb..0e2714d4f42b4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java @@ -16,6 +16,9 @@ package com.android.systemui.statusbar; +import static com.android.systemui.statusbar.StatusBarIconView.STATE_DOT; +import static com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN; +import static com.android.systemui.statusbar.StatusBarIconView.STATE_ICON; import static com.android.systemui.statusbar.policy.DarkIconDispatcher.getTint; import static com.android.systemui.statusbar.policy.DarkIconDispatcher.isInArea; @@ -24,11 +27,17 @@ import android.content.res.ColorStateList; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.util.AttributeSet; +import android.util.Log; import android.view.ContextThemeWrapper; +import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; +import android.widget.FrameLayout; import android.widget.ImageView; +import android.widget.LinearLayout; import com.android.keyguard.AlphaOptimizedLinearLayout; import com.android.settingslib.Utils; import com.android.systemui.R; @@ -38,10 +47,14 @@ import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver; /** * Start small: StatusBarWifiView will be able to layout from a WifiIconState */ -public class StatusBarWifiView extends AlphaOptimizedLinearLayout implements DarkReceiver, +public class StatusBarWifiView extends FrameLayout implements DarkReceiver, StatusIconDisplayable { private static final String TAG = "StatusBarWifiView"; + /// Used to show etc dots + private StatusBarIconView mDotView; + /// Contains the main icon layout + private LinearLayout mWifiGroup; private ImageView mWifiIcon; private ImageView mIn; private ImageView mOut; @@ -55,9 +68,12 @@ public class StatusBarWifiView extends AlphaOptimizedLinearLayout implements Dar private ContextThemeWrapper mDarkContext; private ContextThemeWrapper mLightContext; - public static StatusBarWifiView fromContext(Context context) { + public static StatusBarWifiView fromContext(Context context, String slot) { LayoutInflater inflater = LayoutInflater.from(context); - return (StatusBarWifiView) inflater.inflate(R.layout.status_bar_wifi_group, null); + StatusBarWifiView v = (StatusBarWifiView) inflater.inflate(R.layout.status_bar_wifi_group, null); + v.setSlot(slot); + v.init(); + return v; } public StatusBarWifiView(Context context) { @@ -77,12 +93,6 @@ public class StatusBarWifiView extends AlphaOptimizedLinearLayout implements Dar super(context, attrs, defStyleAttr, defStyleRes); } - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - init(); - } - public void setSlot(String slot) { mSlot = slot; } @@ -93,6 +103,12 @@ public class StatusBarWifiView extends AlphaOptimizedLinearLayout implements Dar mWifiIcon.setImageTintList(list); mIn.setImageTintList(list); mOut.setImageTintList(list); + mDotView.setDecorColor(color); + } + + @Override + public void setDecorColor(int color) { + mDotView.setDecorColor(color); } @Override @@ -105,18 +121,55 @@ public class StatusBarWifiView extends AlphaOptimizedLinearLayout implements Dar return mState != null && mState.visible; } + @Override + public void setVisibleState(int state) { + switch (state) { + case STATE_ICON: + mWifiGroup.setVisibility(View.VISIBLE); + mDotView.setVisibility(View.GONE); + break; + case STATE_DOT: + mWifiGroup.setVisibility(View.GONE); + mDotView.setVisibility(View.VISIBLE); + break; + case STATE_HIDDEN: + default: + setVisibility(View.GONE); + break; + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + int width = MeasureSpec.getSize(widthMeasureSpec); + } + private void init() { int dualToneLightTheme = Utils.getThemeAttr(mContext, R.attr.lightIconTheme); int dualToneDarkTheme = Utils.getThemeAttr(mContext, R.attr.darkIconTheme); mLightContext = new ContextThemeWrapper(mContext, dualToneLightTheme); mDarkContext = new ContextThemeWrapper(mContext, dualToneDarkTheme); + mWifiGroup = findViewById(R.id.wifi_group); mWifiIcon = findViewById(R.id.wifi_signal); mIn = findViewById(R.id.wifi_in); mOut = findViewById(R.id.wifi_out); mSignalSpacer = findViewById(R.id.wifi_signal_spacer); mAirplaneSpacer = findViewById(R.id.wifi_airplane_spacer); mInoutContainer = findViewById(R.id.inout_container); + + initDotView(); + } + + private void initDotView() { + mDotView = new StatusBarIconView(mContext, mSlot, null); + mDotView.setVisibleState(STATE_DOT); + + int width = mContext.getResources().getDimensionPixelSize(R.dimen.status_bar_icon_size); + LayoutParams lp = new LayoutParams(width, width); + lp.gravity = Gravity.CENTER_VERTICAL | Gravity.START; + addView(mDotView, lp); } public void applyWifiState(WifiIconState state) { @@ -186,6 +239,8 @@ public class StatusBarWifiView extends AlphaOptimizedLinearLayout implements Dar } mIn.setImageTintList(ColorStateList.valueOf(getTint(area, this, tint))); mOut.setImageTintList(ColorStateList.valueOf(getTint(area, this, tint))); + mDotView.setDecorColor(tint); + mDotView.setIconColor(tint, false); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java index ccab0d6fa44b8..6383816ead31c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java @@ -21,6 +21,8 @@ import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver; public interface StatusIconDisplayable extends DarkReceiver { String getSlot(); void setStaticDrawableColor(int color); + void setDecorColor(int color); + void setVisibleState(int state); boolean isIconVisible(); default boolean isIconBlocked() { return false; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java index 48540b1116095..824960ee6f830 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java @@ -198,8 +198,7 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da public void addDemoWifiView(WifiIconState state) { Log.d(TAG, "addDemoWifiView: "); - StatusBarWifiView view = StatusBarWifiView.fromContext(mContext); - view.setSlot(state.slot); + StatusBarWifiView view = StatusBarWifiView.fromContext(mContext, state.slot); int viewIndex = getChildCount(); // If we have mobile views, put wifi before them diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java index dc3a752a26949..b693af5cb2639 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java @@ -204,12 +204,6 @@ public class KeyguardStatusBarView extends RelativeLayout } } - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - updateLayoutConsideringCutout(); - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - } - @Override public WindowInsets onApplyWindowInsets(WindowInsets insets) { mLayoutState = LAYOUT_NONE; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java index 94e004bd1b47d..1ba37a9bf9dd5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java @@ -163,6 +163,7 @@ public interface StatusBarIconController { StatusBarIconHolder holder) { StatusIconDisplayable view = addHolder(index, slot, blocked, holder); view.setStaticDrawableColor(mColor); + view.setDecorColor(mColor); } public void setTint(int color) { @@ -172,6 +173,7 @@ public interface StatusBarIconController { if (child instanceof StatusIconDisplayable) { StatusIconDisplayable icon = (StatusIconDisplayable) child; icon.setStaticDrawableColor(mColor); + icon.setDecorColor(mColor); } } } @@ -289,8 +291,7 @@ public interface StatusBarIconController { } private StatusBarWifiView onCreateStatusBarWifiView(String slot) { - StatusBarWifiView view = StatusBarWifiView.fromContext(mContext); - view.setSlot(slot); + StatusBarWifiView view = StatusBarWifiView.fromContext(mContext, slot); return view; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java index 255e10e2680d3..c97c8ebd05451 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java @@ -16,30 +16,57 @@ package com.android.systemui.statusbar.phone; +import static com.android.systemui.statusbar.StatusBarIconView.STATE_ICON; +import static com.android.systemui.statusbar.StatusBarIconView.STATE_DOT; +import static com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN; + import android.annotation.Nullable; import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Style; import android.util.AttributeSet; import android.util.Log; import android.view.View; +import android.view.ViewGroup; import com.android.keyguard.AlphaOptimizedLinearLayout; import com.android.systemui.R; import com.android.systemui.statusbar.StatusIconDisplayable; import com.android.systemui.statusbar.stack.ViewState; +import java.lang.ref.WeakReference; +import java.util.ArrayList; /** * A container for Status bar system icons. Limits the number of system icons and handles overflow - * similar to NotificationIconController. Can be used to layout nested StatusIconContainers + * similar to {@link NotificationIconContainer}. * - * Children are expected to be of type StatusBarIconView. + * Children are expected to implement {@link StatusIconDisplayable} */ public class StatusIconContainer extends AlphaOptimizedLinearLayout { private static final String TAG = "StatusIconContainer"; private static final boolean DEBUG = false; - private static final int MAX_ICONS = 5; + private static final boolean DEBUG_OVERFLOW = false; + // Max 5 status icons including battery + private static final int MAX_ICONS = 4; private static final int MAX_DOTS = 3; + private int mDotPadding; + private int mStaticDotDiameter; + private int mUnderflowWidth; + private int mUnderflowStart = 0; + // Whether or not we can draw into the underflow space + private boolean mNeedsUnderflow; + // Individual StatusBarIconViews draw their etc dots centered in this width + private int mIconDotFrameWidth; + private boolean mShouldRestrictIcons = true; + // Used to count which states want to be visible during layout + private ArrayList mLayoutStates = new ArrayList<>(); + // So we can count and measure properly + private ArrayList mMeasureViews = new ArrayList<>(); + public StatusIconContainer(Context context) { this(context, null); } @@ -48,6 +75,27 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { super(context, attrs); } + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + setWillNotDraw(!DEBUG_OVERFLOW); + initDimens(); + } + + public void setShouldRestrictIcons(boolean should) { + mShouldRestrictIcons = should; + } + + private void initDimens() { + // This is the same value that StatusBarIconView uses + mIconDotFrameWidth = getResources().getDimensionPixelSize( + com.android.internal.R.dimen.status_bar_icon_size); + mDotPadding = getResources().getDimensionPixelSize(R.dimen.overflow_icon_dot_padding); + int radius = getResources().getDimensionPixelSize(R.dimen.overflow_dot_radius); + mStaticDotDiameter = 2 * radius; + mUnderflowWidth = mIconDotFrameWidth + 2 * (mStaticDotDiameter + mDotPadding); + } + @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { float midY = getHeight() / 2.0f; @@ -66,24 +114,81 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { applyIconStates(); } + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + if (DEBUG_OVERFLOW) { + Paint paint = new Paint(); + paint.setStyle(Style.STROKE); + paint.setColor(Color.RED); + + // Show bounding box + canvas.drawRect(getPaddingStart(), 0, getWidth() - getPaddingEnd(), getHeight(), paint); + + // Show etc box + paint.setColor(Color.GREEN); + canvas.drawRect( + mUnderflowStart, 0, mUnderflowStart + mUnderflowWidth, getHeight(), paint); + } + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - - int width = MeasureSpec.getSize(widthMeasureSpec); - int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.UNSPECIFIED); - + mMeasureViews.clear(); + int mode = MeasureSpec.getMode(widthMeasureSpec); + final int width = MeasureSpec.getSize(widthMeasureSpec); final int count = getChildCount(); - // Measure all children so that they report the correct width + // Collect all of the views which want to be laid out for (int i = 0; i < count; i++) { - measureChild(getChildAt(i), widthSpec, heightMeasureSpec); + StatusIconDisplayable icon = (StatusIconDisplayable) getChildAt(i); + if (icon.isIconVisible() && !icon.isIconBlocked()) { + mMeasureViews.add((View) icon); + } + } + + int visibleCount = mMeasureViews.size(); + int maxVisible = visibleCount <= MAX_ICONS ? MAX_ICONS : MAX_ICONS - 1; + int totalWidth = getPaddingStart() + getPaddingEnd(); + boolean trackWidth = true; + + // Measure all children so that they report the correct width + int childWidthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.UNSPECIFIED); + mNeedsUnderflow = mShouldRestrictIcons && visibleCount > MAX_ICONS; + for (int i = 0; i < mMeasureViews.size(); i++) { + // Walking backwards + View child = mMeasureViews.get(visibleCount - i - 1); + measureChild(child, childWidthSpec, heightMeasureSpec); + if (mShouldRestrictIcons) { + if (i < maxVisible && trackWidth) { + totalWidth += getViewTotalMeasuredWidth(child); + } else if (trackWidth) { + // We've hit the icon limit; add space for dots + totalWidth += mUnderflowWidth; + trackWidth = false; + } + } else { + totalWidth += getViewTotalMeasuredWidth(child); + } + } + + if (mode == MeasureSpec.EXACTLY) { + if (!mNeedsUnderflow && totalWidth > width) { + mNeedsUnderflow = true; + } + setMeasuredDimension(width, MeasureSpec.getSize(heightMeasureSpec)); + } else { + if (mode == MeasureSpec.AT_MOST && totalWidth > width) { + mNeedsUnderflow = true; + totalWidth = width; + } + setMeasuredDimension(totalWidth, MeasureSpec.getSize(heightMeasureSpec)); } } @Override public void onViewAdded(View child) { super.onViewAdded(child); - ViewState vs = new ViewState(); + StatusIconState vs = new StatusIconState(); child.setTag(R.id.status_bar_view_state_tag, vs); } @@ -97,6 +202,7 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { * Layout is happening from end -> start */ private void calculateIconTranslations() { + mLayoutStates.clear(); float width = getWidth() - getPaddingEnd(); float translationX = width; float contentStart = getPaddingStart(); @@ -106,45 +212,57 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { if (DEBUG) android.util.Log.d(TAG, "calculateIconTransitions: start=" + translationX + " width=" + width); - //TODO: Dots + // Collect all of the states which want to be visible for (int i = childCount - 1; i >= 0; i--) { View child = getChildAt(i); - if (!(child instanceof StatusIconDisplayable)) { - if (DEBUG) Log.d(TAG, "skipping child (wrong type)"); - continue; - } - StatusIconDisplayable iconView = (StatusIconDisplayable) child; - - ViewState childState = getViewStateFromChild(child); - if (childState == null ) { - if (DEBUG) Log.d(TAG, "skipping child (" + iconView.getSlot() + ") no ViewState"); - continue; - } + StatusIconState childState = getViewStateFromChild(child); if (!iconView.isIconVisible() || iconView.isIconBlocked()) { - childState.hidden = true; + childState.visibleState = STATE_HIDDEN; if (DEBUG) Log.d(TAG, "skipping child (" + iconView.getSlot() + ") not visible"); continue; } - childState.xTranslation = translationX - child.getWidth(); + childState.visibleState = STATE_ICON; + childState.xTranslation = translationX - getViewTotalWidth(child); + mLayoutStates.add(0, childState); - if (childState.xTranslation < contentStart) { - if (firstUnderflowIndex == -1) { - firstUnderflowIndex = i; - } + translationX -= getViewTotalWidth(child); + } + + // Show either 1-4 dots, or 3 dots + overflow + int totalVisible = mLayoutStates.size(); + int maxVisible = totalVisible <= MAX_ICONS ? MAX_ICONS : MAX_ICONS - 1; + + mUnderflowStart = 0; + int visible = 0; + firstUnderflowIndex = -1; + for (int i = totalVisible - 1; i >= 0; i--) { + StatusIconState state = mLayoutStates.get(i); + // Allow room for underflow if we found we need it in onMeasure + if (mNeedsUnderflow && (state.xTranslation < (contentStart + mUnderflowWidth))|| + (mShouldRestrictIcons && visible >= maxVisible)) { + firstUnderflowIndex = i; + break; } - - translationX -= child.getWidth(); + mUnderflowStart = (int) Math.max(contentStart, state.xTranslation - mUnderflowWidth); + visible++; } if (firstUnderflowIndex != -1) { - for (int i = 0; i <= firstUnderflowIndex; i++) { - View child = getChildAt(i); - ViewState vs = getViewStateFromChild(child); - if (vs != null) { - vs.hidden = true; + int totalDots = 0; + int dotWidth = mStaticDotDiameter + mDotPadding; + int dotOffset = mUnderflowStart + mUnderflowWidth - mIconDotFrameWidth; + for (int i = firstUnderflowIndex; i >= 0; i--) { + StatusIconState state = mLayoutStates.get(i); + if (totalDots < MAX_DOTS) { + state.xTranslation = dotOffset; + state.visibleState = STATE_DOT; + dotOffset -= dotWidth; + totalDots++; + } else { + state.visibleState = STATE_HIDDEN; } } } @@ -153,7 +271,7 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { if (isLayoutRtl()) { for (int i = 0; i < childCount; i++) { View child = getChildAt(i); - ViewState state = getViewStateFromChild(child); + StatusIconState state = getViewStateFromChild(child); state.xTranslation = width - state.xTranslation - child.getWidth(); } } @@ -162,7 +280,7 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { private void applyIconStates() { for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); - ViewState vs = getViewStateFromChild(child); + StatusIconState vs = getViewStateFromChild(child); if (vs != null) { vs.applyToView(child); } @@ -172,7 +290,7 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { private void resetViewStates() { for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); - ViewState vs = getViewStateFromChild(child); + StatusIconState vs = getViewStateFromChild(child); if (vs == null) { continue; } @@ -187,7 +305,29 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { } } - private static @Nullable ViewState getViewStateFromChild(View child) { - return (ViewState) child.getTag(R.id.status_bar_view_state_tag); + private static @Nullable StatusIconState getViewStateFromChild(View child) { + return (StatusIconState) child.getTag(R.id.status_bar_view_state_tag); + } + + private static int getViewTotalMeasuredWidth(View child) { + return child.getMeasuredWidth() + child.getPaddingStart() + child.getPaddingEnd(); + } + + private static int getViewTotalWidth(View child) { + return child.getWidth() + child.getPaddingStart() + child.getPaddingEnd(); + } + + public static class StatusIconState extends ViewState { + /// StatusBarIconView.STATE_* + public int visibleState = STATE_ICON; + + @Override + public void applyToView(View view) { + if (view instanceof StatusIconDisplayable) { + StatusIconDisplayable icon = (StatusIconDisplayable) view; + icon.setVisibleState(visibleState); + } + super.applyToView(view); + } } }