Merge "Make sure all Notification actions are shown" into nyc-dev
This commit is contained in:
@@ -3412,10 +3412,6 @@ public class Notification implements Parcelable
|
||||
validRemoteInput |= hasValidRemoteInput(action);
|
||||
|
||||
final RemoteViews button = generateActionButton(action);
|
||||
if (i == N - 1) {
|
||||
button.setViewLayoutWidth(com.android.internal.R.id.action0,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT);
|
||||
}
|
||||
big.addView(R.id.actions, button);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Pair;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.RemoteViews;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* Layout for notification actions that ensures that no action consumes more than their share of
|
||||
* the remaining available width, and the last action consumes the remaining space.
|
||||
*/
|
||||
@RemoteViews.RemoteView
|
||||
public class NotificationActionListLayout extends ViewGroup {
|
||||
|
||||
private int mTotalWidth = 0;
|
||||
private ArrayList<Pair<Integer, TextView>> mMeasureOrderTextViews = new ArrayList<>();
|
||||
private ArrayList<View> mMeasureOrderOther = new ArrayList<>();
|
||||
|
||||
public NotificationActionListLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
final int N = getChildCount();
|
||||
int textViews = 0;
|
||||
int otherViews = 0;
|
||||
int notGoneChildren = 0;
|
||||
|
||||
View lastNotGoneChild = null;
|
||||
for (int i = 0; i < N; i++) {
|
||||
View c = getChildAt(i);
|
||||
if (c instanceof TextView) {
|
||||
textViews++;
|
||||
} else {
|
||||
otherViews++;
|
||||
}
|
||||
if (c.getVisibility() != GONE) {
|
||||
notGoneChildren++;
|
||||
lastNotGoneChild = c;
|
||||
}
|
||||
}
|
||||
|
||||
// Rebuild the measure order if the number of children changed or the text length of
|
||||
// any of the children changed.
|
||||
boolean needRebuild = false;
|
||||
if (textViews != mMeasureOrderTextViews.size()
|
||||
|| otherViews != mMeasureOrderOther.size()) {
|
||||
needRebuild = true;
|
||||
}
|
||||
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()) {
|
||||
needRebuild = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (notGoneChildren > 1 && needRebuild) {
|
||||
rebuildMeasureOrder(textViews, otherViews);
|
||||
}
|
||||
|
||||
final boolean constrained =
|
||||
MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED;
|
||||
|
||||
final int innerWidth = MeasureSpec.getSize(widthMeasureSpec) - mPaddingLeft - mPaddingRight;
|
||||
final int otherSize = mMeasureOrderOther.size();
|
||||
int usedWidth = 0;
|
||||
|
||||
// Optimization: Don't do this if there's only one child.
|
||||
int measuredChildren = 0;
|
||||
for (int i = 0; i < N && notGoneChildren > 1; i++) {
|
||||
// Measure shortest children first. To avoid measuring twice, we approximate by looking
|
||||
// at the text length.
|
||||
View c;
|
||||
if (i < otherSize) {
|
||||
c = mMeasureOrderOther.get(i);
|
||||
} else {
|
||||
c = mMeasureOrderTextViews.get(i - otherSize).second;
|
||||
}
|
||||
if (c.getVisibility() == GONE) {
|
||||
continue;
|
||||
}
|
||||
MarginLayoutParams lp = (MarginLayoutParams) c.getLayoutParams();
|
||||
|
||||
int usedWidthForChild = usedWidth;
|
||||
if (constrained) {
|
||||
// Make sure that this child doesn't consume more than its share of the remaining
|
||||
// total available space. Not used space will benefit subsequent views. Since we
|
||||
// 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);
|
||||
|
||||
usedWidthForChild = innerWidth - maxWidthForChild;
|
||||
}
|
||||
|
||||
measureChildWithMargins(c, widthMeasureSpec, usedWidthForChild,
|
||||
heightMeasureSpec, 0 /* usedHeight */);
|
||||
|
||||
usedWidth += c.getMeasuredWidth() + lp.rightMargin + lp.leftMargin;
|
||||
measuredChildren++;
|
||||
}
|
||||
|
||||
// Make sure to measure the last child full-width if we didn't use up the entire width,
|
||||
// or we didn't measure yet because there's just one child.
|
||||
if (lastNotGoneChild != null && (constrained && usedWidth < innerWidth
|
||||
|| notGoneChildren == 1)) {
|
||||
MarginLayoutParams lp = (MarginLayoutParams) lastNotGoneChild.getLayoutParams();
|
||||
if (notGoneChildren > 1) {
|
||||
// Need to make room, since we already measured this once.
|
||||
usedWidth -= lastNotGoneChild.getMeasuredWidth() + lp.rightMargin + lp.leftMargin;
|
||||
}
|
||||
|
||||
int originalWidth = lp.width;
|
||||
lp.width = LayoutParams.MATCH_PARENT;
|
||||
measureChildWithMargins(lastNotGoneChild, widthMeasureSpec, usedWidth,
|
||||
heightMeasureSpec, 0 /* usedHeight */);
|
||||
lp.width = originalWidth;
|
||||
|
||||
usedWidth += lastNotGoneChild.getMeasuredWidth() + lp.rightMargin + lp.leftMargin;
|
||||
}
|
||||
|
||||
mTotalWidth = usedWidth + mPaddingRight + mPaddingLeft;
|
||||
setMeasuredDimension(resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec),
|
||||
resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec));
|
||||
}
|
||||
|
||||
private void rebuildMeasureOrder(int capacityText, int capacityOther) {
|
||||
clearMeasureOrder();
|
||||
mMeasureOrderTextViews.ensureCapacity(capacityText);
|
||||
mMeasureOrderOther.ensureCapacity(capacityOther);
|
||||
final int childCount = getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
View c = getChildAt(i);
|
||||
if (c instanceof TextView && ((TextView) c).getText().length() > 0) {
|
||||
mMeasureOrderTextViews.add(Pair.create(((TextView) c).getText().length(),
|
||||
(TextView)c));
|
||||
} else {
|
||||
mMeasureOrderOther.add(c);
|
||||
}
|
||||
}
|
||||
mMeasureOrderTextViews.sort(MEASURE_ORDER_COMPARATOR);
|
||||
}
|
||||
|
||||
private void clearMeasureOrder() {
|
||||
mMeasureOrderOther.clear();
|
||||
mMeasureOrderTextViews.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewAdded(View child) {
|
||||
super.onViewAdded(child);
|
||||
clearMeasureOrder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewRemoved(View child) {
|
||||
super.onViewRemoved(child);
|
||||
clearMeasureOrder();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
final boolean isLayoutRtl = isLayoutRtl();
|
||||
final int paddingTop = mPaddingTop;
|
||||
|
||||
int childTop;
|
||||
int childLeft;
|
||||
|
||||
// Where bottom of child should go
|
||||
final int height = bottom - top;
|
||||
|
||||
// Space available for child
|
||||
int innerHeight = height - paddingTop - mPaddingBottom;
|
||||
|
||||
final int count = getChildCount();
|
||||
|
||||
final int layoutDirection = getLayoutDirection();
|
||||
switch (Gravity.getAbsoluteGravity(Gravity.START, layoutDirection)) {
|
||||
case Gravity.RIGHT:
|
||||
// mTotalWidth contains the padding already
|
||||
childLeft = mPaddingLeft + right - left - mTotalWidth;
|
||||
break;
|
||||
|
||||
case Gravity.LEFT:
|
||||
default:
|
||||
childLeft = mPaddingLeft;
|
||||
break;
|
||||
}
|
||||
|
||||
int start = 0;
|
||||
int dir = 1;
|
||||
//In case of RTL, start drawing from the last child.
|
||||
if (isLayoutRtl) {
|
||||
start = count - 1;
|
||||
dir = -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
final int childIndex = start + dir * i;
|
||||
final View child = getChildAt(childIndex);
|
||||
if (child.getVisibility() != GONE) {
|
||||
final int childWidth = child.getMeasuredWidth();
|
||||
final int childHeight = child.getMeasuredHeight();
|
||||
|
||||
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
|
||||
|
||||
childTop = paddingTop + ((innerHeight - childHeight) / 2)
|
||||
+ lp.topMargin - lp.bottomMargin;
|
||||
|
||||
childLeft += lp.leftMargin;
|
||||
child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
|
||||
childLeft += childWidth + lp.rightMargin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LayoutParams generateLayoutParams(AttributeSet attrs) {
|
||||
return new MarginLayoutParams(getContext(), attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LayoutParams generateDefaultLayoutParams() {
|
||||
return new MarginLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LayoutParams generateLayoutParams(LayoutParams p) {
|
||||
if (p instanceof MarginLayoutParams) {
|
||||
return new MarginLayoutParams((MarginLayoutParams)p);
|
||||
}
|
||||
return new MarginLayoutParams(p);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean checkLayoutParams(LayoutParams p) {
|
||||
return p instanceof MarginLayoutParams;
|
||||
}
|
||||
|
||||
public static final Comparator<Pair<Integer, TextView>> MEASURE_ORDER_COMPARATOR
|
||||
= (a, b) -> a.first.compareTo(b.first);
|
||||
}
|
||||
@@ -18,7 +18,7 @@
|
||||
android:id="@+id/actions_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<LinearLayout
|
||||
<com.android.internal.widget.NotificationActionListLayout
|
||||
android:id="@+id/actions"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
@@ -29,5 +29,5 @@
|
||||
android:background="@color/notification_action_list"
|
||||
>
|
||||
<!-- actions will be added here -->
|
||||
</LinearLayout>
|
||||
</com.android.internal.widget.NotificationActionListLayout>
|
||||
</FrameLayout>
|
||||
|
||||
Reference in New Issue
Block a user