Fix measure logic so that priority children can have more than 1/N space.

The priority previously just put the view at the front of the list, which ensured that super long user-provided actions couldn't cause the priority action at the end to be truncated.  However, this still meant that a priority action at the start whose text would have it be longer than 1/3 of the action space would be capped at 1/3 instead of taking on some of the user-defined child's space.

Bug: 187533622
Test: Post Notify2 app's Call style w/ Screen action; notice that "Decline" is not truncated.

Change-Id: Ia18b2dc366ba7cac13ed845acac59d89206e7d5e
This commit is contained in:
Jeff DeCew
2021-05-10 12:12:21 -04:00
parent 2ca6b4399a
commit c6d567d239

View File

@@ -17,11 +17,11 @@
package com.android.internal.widget;
import android.annotation.DimenRes;
import android.app.Notification;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.RippleDrawable;
import android.util.AttributeSet;
import android.util.Pair;
import android.view.Gravity;
import android.view.RemotableViewMethod;
import android.view.View;
@@ -43,7 +43,7 @@ public class NotificationActionListLayout extends LinearLayout {
private final int mGravity;
private int mTotalWidth = 0;
private int mExtraStartPadding = 0;
private ArrayList<Pair<Integer, TextView>> mMeasureOrderTextViews = new ArrayList<>();
private ArrayList<TextViewInfo> mMeasureOrderTextViews = new ArrayList<>();
private ArrayList<View> mMeasureOrderOther = new ArrayList<>();
private boolean mEmphasizedMode;
private int mDefaultPaddingBottom;
@@ -69,12 +69,18 @@ public class NotificationActionListLayout extends LinearLayout {
ta.recycle();
}
private static boolean isPriority(View actionView) {
return actionView instanceof EmphasizedNotificationButton
&& ((EmphasizedNotificationButton) actionView).isPriority();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int N = getChildCount();
int textViews = 0;
int otherViews = 0;
int notGoneChildren = 0;
int priorityChildren = 0;
for (int i = 0; i < N; i++) {
View c = getChildAt(i);
@@ -85,6 +91,9 @@ public class NotificationActionListLayout extends LinearLayout {
}
if (c.getVisibility() != GONE) {
notGoneChildren++;
if (isPriority(c)) {
priorityChildren++;
}
}
}
@@ -98,9 +107,9 @@ public class NotificationActionListLayout extends LinearLayout {
if (!needRebuild) {
final int size = mMeasureOrderTextViews.size();
for (int i = 0; i < size; i++) {
Pair<Integer, TextView> pair = mMeasureOrderTextViews.get(i);
if (pair.first != pair.second.getText().length()) {
if (mMeasureOrderTextViews.get(i).needsRebuild()) {
needRebuild = true;
break;
}
}
}
@@ -117,14 +126,19 @@ public class NotificationActionListLayout extends LinearLayout {
int usedWidth = 0;
int measuredChildren = 0;
int measuredPriorityChildren = 0;
for (int i = 0; i < N; i++) {
// Measure shortest children first. To avoid measuring twice, we approximate by looking
// at the text length.
View c;
final boolean isPriority;
final View c;
if (i < otherSize) {
c = mMeasureOrderOther.get(i);
isPriority = false;
} else {
c = mMeasureOrderTextViews.get(i - otherSize).second;
TextViewInfo info = mMeasureOrderTextViews.get(i - otherSize);
c = info.mTextView;
isPriority = info.mIsPriority;
}
if (c.getVisibility() == GONE) {
continue;
@@ -138,7 +152,18 @@ public class NotificationActionListLayout extends LinearLayout {
// measure in the order of (approx.) size, a large view can still take more than its
// share if the others are small.
int availableWidth = innerWidth - usedWidth;
int maxWidthForChild = availableWidth / (notGoneChildren - measuredChildren);
int unmeasuredChildren = notGoneChildren - measuredChildren;
int maxWidthForChild = availableWidth / unmeasuredChildren;
if (isPriority) {
// Priority children get a larger maximum share of the total space:
// maximum priority share = (nPriority + 1) / (MAX + 1)
int unmeasuredPriorityChildren = priorityChildren - measuredPriorityChildren;
int unmeasuredOtherChildren = unmeasuredChildren - unmeasuredPriorityChildren;
int widthReservedForOtherChildren = innerWidth * unmeasuredOtherChildren
/ (Notification.MAX_ACTION_BUTTONS + 1);
int widthAvailableForPriority = availableWidth - widthReservedForOtherChildren;
maxWidthForChild = widthAvailableForPriority / unmeasuredPriorityChildren;
}
usedWidthForChild = innerWidth - maxWidthForChild;
}
@@ -148,6 +173,9 @@ public class NotificationActionListLayout extends LinearLayout {
usedWidth += c.getMeasuredWidth() + lp.rightMargin + lp.leftMargin;
measuredChildren++;
if (isPriority) {
measuredPriorityChildren++;
}
}
int collapsibleIndent = mCollapsibleIndentDimen == 0 ? 0
@@ -170,12 +198,8 @@ public class NotificationActionListLayout extends LinearLayout {
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View c = getChildAt(i);
if (isPriority(c)) {
// add with 0 length to ensure that this view is measured before others.
mMeasureOrderTextViews.add(Pair.create(0, (TextView) c));
} else if (c instanceof TextView && ((TextView) c).getText().length() > 0) {
mMeasureOrderTextViews.add(Pair.create(((TextView) c).getText().length(),
(TextView)c));
if (c instanceof TextView && ((TextView) c).getText().length() > 0) {
mMeasureOrderTextViews.add(new TextViewInfo((TextView) c));
} else {
mMeasureOrderOther.add(c);
}
@@ -333,6 +357,28 @@ public class NotificationActionListLayout extends LinearLayout {
return 0;
}
public static final Comparator<Pair<Integer, TextView>> MEASURE_ORDER_COMPARATOR
= (a, b) -> a.first.compareTo(b.first);
public static final Comparator<TextViewInfo> MEASURE_ORDER_COMPARATOR = (a, b) -> {
int priorityComparison = -Boolean.compare(a.mIsPriority, b.mIsPriority);
return priorityComparison != 0
? priorityComparison
: Integer.compare(a.mTextLength, b.mTextLength);
};
private static final class TextViewInfo {
final boolean mIsPriority;
final int mTextLength;
final TextView mTextView;
TextViewInfo(TextView textView) {
this.mIsPriority = isPriority(textView);
this.mTextLength = textView.getText().length();
this.mTextView = textView;
}
boolean needsRebuild() {
return mTextView.getText().length() != mTextLength
|| isPriority(mTextView) != mIsPriority;
}
}
}