Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9cb7652f authored by Adrian Roos's avatar Adrian Roos Committed by Android (Google) Code Review
Browse files

Merge "Make sure all Notification actions are shown" into nyc-dev

parents 7cae9f7b 61254356
Loading
Loading
Loading
Loading
+0 −4
Original line number Original line Diff line number Diff line
@@ -3412,10 +3412,6 @@ public class Notification implements Parcelable
                    validRemoteInput |= hasValidRemoteInput(action);
                    validRemoteInput |= hasValidRemoteInput(action);


                    final RemoteViews button = generateActionButton(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);
                    big.addView(R.id.actions, button);
                }
                }
            } else {
            } else {
+268 −0
Original line number Original line Diff line number Diff line
/*
 * 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);
}
+2 −2
Original line number Original line Diff line number Diff line
@@ -18,7 +18,7 @@
        android:id="@+id/actions_container"
        android:id="@+id/actions_container"
        android:layout_width="match_parent"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        android:layout_height="wrap_content">
    <LinearLayout
    <com.android.internal.widget.NotificationActionListLayout
            android:id="@+id/actions"
            android:id="@+id/actions"
            android:layout_width="match_parent"
            android:layout_width="match_parent"
            android:layout_height="56dp"
            android:layout_height="56dp"
@@ -29,5 +29,5 @@
            android:background="@color/notification_action_list"
            android:background="@color/notification_action_list"
            >
            >
        <!-- actions will be added here -->
        <!-- actions will be added here -->
    </LinearLayout>
    </com.android.internal.widget.NotificationActionListLayout>
</FrameLayout>
</FrameLayout>