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

Commit 90b22b36 authored by Jeff DeCew's avatar Jeff DeCew Committed by Android (Google) Code Review
Browse files

Merge changes I80173501,I6fbacf86,I4995288d,Ie21b0d9d

* changes:
  Split NotificationHeaderView's text content into NotificationTopLineView
  Fix QS Horizontal Margin
  Remove unused gravity customization
  Remove unused flag in NotificationHeaderView
parents a26922c4 085a9fc9
Loading
Loading
Loading
Loading
+16 −66
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@ import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Outline;
import android.graphics.Rect;
@@ -44,24 +43,16 @@ import java.util.ArrayList;
public class NotificationHeaderView extends ViewGroup {
    private final int mChildMinWidth;
    private final int mContentEndMargin;
    private final int mGravity;
    private View mAppName;
    private View mHeaderText;
    private View mSecondaryHeaderText;
    private OnClickListener mExpandClickListener;
    private OnClickListener mFeedbackListener;
    private HeaderTouchListener mTouchListener = new HeaderTouchListener();
    private NotificationTopLineView mTopLineView;
    private NotificationExpandButton mExpandButton;
    private CachingIconView mIcon;
    private View mProfileBadge;
    private View mFeedbackIcon;
    private boolean mShowWorkBadgeAtEnd;
    private int mHeaderTextMarginEnd;
    private Drawable mBackground;
    private boolean mEntireHeaderClickable;
    private boolean mExpandOnlyOnButton;
    private boolean mAcceptAllTouches;
    private int mTotalWidth;

    ViewOutlineProvider mProvider = new ViewOutlineProvider() {
        @Override
@@ -93,23 +84,14 @@ public class NotificationHeaderView extends ViewGroup {
        mChildMinWidth = res.getDimensionPixelSize(R.dimen.notification_header_shrink_min_width);
        mContentEndMargin = res.getDimensionPixelSize(R.dimen.notification_content_margin_end);
        mEntireHeaderClickable = res.getBoolean(R.bool.config_notificationHeaderClickableForExpand);

        int[] attrIds = {android.R.attr.gravity};
        TypedArray ta = context.obtainStyledAttributes(attrs, attrIds, defStyleAttr, defStyleRes);
        mGravity = ta.getInt(0, 0);
        ta.recycle();
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mAppName = findViewById(R.id.app_name_text);
        mHeaderText = findViewById(R.id.header_text);
        mSecondaryHeaderText = findViewById(R.id.header_text_secondary);
        mExpandButton = findViewById(R.id.expand_button);
        mIcon = findViewById(R.id.icon);
        mProfileBadge = findViewById(R.id.profile_badge);
        mFeedbackIcon = findViewById(R.id.feedback);
        mTopLineView = findViewById(R.id.notification_top_line);
        mExpandButton = findViewById(R.id.expand_button);
        setClipToPadding(false);
    }

@@ -136,7 +118,7 @@ public class NotificationHeaderView extends ViewGroup {
                    lp.topMargin + lp.bottomMargin, lp.height);
            child.measure(childWidthSpec, childHeightSpec);
            // Icons that should go at the end
            if (child == mExpandButton || child == mProfileBadge || child == mFeedbackIcon) {
            if (child == mExpandButton) {
                iconWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth();
            } else {
                totalWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth();
@@ -147,19 +129,10 @@ public class NotificationHeaderView extends ViewGroup {
        int endMargin = Math.max(mHeaderTextMarginEnd, iconWidth);
        if (totalWidth > givenWidth - endMargin) {
            int overFlow = totalWidth - givenWidth + endMargin;
            // We are overflowing, lets shrink the app name first
            overFlow = shrinkViewForOverflow(wrapContentHeightSpec, overFlow, mAppName,
            // We are overflowing; shrink the top line
            shrinkViewForOverflow(wrapContentHeightSpec, overFlow, mTopLineView,
                    mChildMinWidth);

            // still overflowing, we shrink the header text
            overFlow = shrinkViewForOverflow(wrapContentHeightSpec, overFlow, mHeaderText, 0);

            // still overflowing, finally we shrink the secondary header text
            shrinkViewForOverflow(wrapContentHeightSpec, overFlow, mSecondaryHeaderText,
                    0);
        }
        totalWidth += getPaddingEnd();
        mTotalWidth = Math.min(totalWidth, givenWidth);
        setMeasuredDimension(givenWidth, givenHeight);
    }

@@ -180,10 +153,6 @@ public class NotificationHeaderView extends ViewGroup {
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int left = getPaddingStart();
        int end = getMeasuredWidth();
        final boolean centerAligned = (mGravity & Gravity.CENTER_HORIZONTAL) != 0;
        if (centerAligned) {
            left += getMeasuredWidth() / 2 - mTotalWidth / 2;
        }
        int childCount = getChildCount();
        int ownHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
        for (int i = 0; i < childCount; i++) {
@@ -198,7 +167,7 @@ public class NotificationHeaderView extends ViewGroup {
            int top = (int) (getPaddingTop() + (ownHeight - childHeight) / 2.0f);
            int bottom = top + childHeight;
            // Icons that should go at the end
            if (child == mExpandButton || child == mProfileBadge || child == mFeedbackIcon) {
            if (child == mExpandButton) {
                if (end == getMeasuredWidth()) {
                    layoutRight = end - mContentEndMargin;
                } else {
@@ -266,7 +235,7 @@ public class NotificationHeaderView extends ViewGroup {
    }

    private void updateTouchListener() {
        if (mExpandClickListener == null && mFeedbackListener == null) {
        if (mExpandClickListener == null) {
            setOnTouchListener(null);
            return;
        }
@@ -274,15 +243,6 @@ public class NotificationHeaderView extends ViewGroup {
        mTouchListener.bindTouchRects();
    }

    /**
     * Sets onclick listener for feedback icon.
     */
    public void setFeedbackOnClickListener(OnClickListener l) {
        mFeedbackListener = l;
        mFeedbackIcon.setOnClickListener(mFeedbackListener);
        updateTouchListener();
    }

    @Override
    public void setOnClickListener(@Nullable OnClickListener l) {
        mExpandClickListener = l;
@@ -290,16 +250,6 @@ public class NotificationHeaderView extends ViewGroup {
        updateTouchListener();
    }

    /**
     * Sets whether or not the work badge appears at the end of the NotificationHeaderView.
     * The expand button will always be closer to the end.
     */
    public void setShowWorkBadgeAtEnd(boolean showWorkBadgeAtEnd) {
        if (showWorkBadgeAtEnd != mShowWorkBadgeAtEnd) {
            mShowWorkBadgeAtEnd = showWorkBadgeAtEnd;
        }
    }

    /**
     * Sets the margin end for the text portion of the header, excluding right-aligned elements
     * @param headerTextMarginEnd margin size
@@ -327,7 +277,6 @@ public class NotificationHeaderView extends ViewGroup {

        private final ArrayList<Rect> mTouchRects = new ArrayList<>();
        private Rect mExpandButtonRect;
        private Rect mFeedbackRect;
        private int mTouchSlop;
        private boolean mTrackGesture;
        private float mDownX;
@@ -340,7 +289,6 @@ public class NotificationHeaderView extends ViewGroup {
            mTouchRects.clear();
            addRectAroundView(mIcon);
            mExpandButtonRect = addRectAroundView(mExpandButton);
            mFeedbackRect = addRectAroundView(mFeedbackIcon);
            addWidthRect();
            mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
        }
@@ -401,11 +349,11 @@ public class NotificationHeaderView extends ViewGroup {
                    break;
                case MotionEvent.ACTION_UP:
                    if (mTrackGesture) {
                        if (mFeedbackIcon.isVisibleToUser()
                                && (mFeedbackRect.contains((int) x, (int) y)
                                || mFeedbackRect.contains((int) mDownX, (int) mDownY))) {
                            mFeedbackIcon.performClick();
                            return true;
                        float topLineX = mTopLineView.getX();
                        float topLineY = mTopLineView.getY();
                        if (mTopLineView.onTouchUp(x - topLineX, y - topLineY,
                                mDownX - topLineX, mDownY - topLineY)) {
                            break;
                        }
                        mExpandButton.performClick();
                    }
@@ -427,7 +375,9 @@ public class NotificationHeaderView extends ViewGroup {
                    return true;
                }
            }
            return false;
            float topLineX = x - mTopLineView.getX();
            float topLineY = y - mTopLineView.getY();
            return mTopLineView.isInTouchRect(topLineX, topLineY);
        }
    }

+344 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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 android.view;

import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.widget.RemoteViews;

import com.android.internal.R;

import java.util.Arrays;
import java.util.List;

/**
 * The top line of content in a notification view.
 * This includes the text views and badges but excludes the icon and the expander.
 *
 * @hide
 */
@RemoteViews.RemoteView
public class NotificationTopLineView extends ViewGroup {
    private final int mChildMinWidth;
    private final int mContentEndMargin;
    private View mAppName;
    private View mHeaderText;
    private View mSecondaryHeaderText;
    private OnClickListener mFeedbackListener;
    private HeaderTouchListener mTouchListener = new HeaderTouchListener();
    private View mProfileBadge;
    private View mFeedbackIcon;
    private int mHeaderTextMarginEnd;
    private List<View> mIconsAtEnd;

    public NotificationTopLineView(Context context) {
        this(context, null);
    }

    public NotificationTopLineView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public NotificationTopLineView(Context context, @Nullable AttributeSet attrs,
            int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    public NotificationTopLineView(Context context, AttributeSet attrs, int defStyleAttr,
            int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        Resources res = getResources();
        mChildMinWidth = res.getDimensionPixelSize(R.dimen.notification_header_shrink_min_width);
        mContentEndMargin = res.getDimensionPixelSize(R.dimen.notification_content_margin_end);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mAppName = findViewById(R.id.app_name_text);
        mHeaderText = findViewById(R.id.header_text);
        mSecondaryHeaderText = findViewById(R.id.header_text_secondary);
        mProfileBadge = findViewById(R.id.profile_badge);
        mFeedbackIcon = findViewById(R.id.feedback);
        mIconsAtEnd = Arrays.asList(mProfileBadge, mFeedbackIcon);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int givenWidth = MeasureSpec.getSize(widthMeasureSpec);
        final int givenHeight = MeasureSpec.getSize(heightMeasureSpec);
        int wrapContentWidthSpec = MeasureSpec.makeMeasureSpec(givenWidth,
                MeasureSpec.AT_MOST);
        int wrapContentHeightSpec = MeasureSpec.makeMeasureSpec(givenHeight,
                MeasureSpec.AT_MOST);
        int totalWidth = getPaddingStart();
        int iconWidth = getPaddingEnd();
        for (int i = 0; i < getChildCount(); i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() == GONE) {
                // We'll give it the rest of the space in the end
                continue;
            }
            final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
            int childWidthSpec = getChildMeasureSpec(wrapContentWidthSpec,
                    lp.leftMargin + lp.rightMargin, lp.width);
            int childHeightSpec = getChildMeasureSpec(wrapContentHeightSpec,
                    lp.topMargin + lp.bottomMargin, lp.height);
            child.measure(childWidthSpec, childHeightSpec);
            // Icons that should go at the end
            if (mIconsAtEnd.contains(child)) {
                iconWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth();
            } else {
                totalWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth();
            }
        }

        // Ensure that there is at least enough space for the icons
        int endMargin = Math.max(mHeaderTextMarginEnd, iconWidth);
        if (totalWidth > givenWidth - endMargin) {
            int overFlow = totalWidth - givenWidth + endMargin;
            // We are overflowing, lets shrink the app name first
            overFlow = shrinkViewForOverflow(wrapContentHeightSpec, overFlow, mAppName,
                    mChildMinWidth);

            // still overflowing, we shrink the header text
            overFlow = shrinkViewForOverflow(wrapContentHeightSpec, overFlow, mHeaderText, 0);

            // still overflowing, finally we shrink the secondary header text
            shrinkViewForOverflow(wrapContentHeightSpec, overFlow, mSecondaryHeaderText,
                    0);
        }
        setMeasuredDimension(givenWidth, givenHeight);
    }

    private int shrinkViewForOverflow(int heightSpec, int overFlow, View targetView,
            int minimumWidth) {
        final int oldWidth = targetView.getMeasuredWidth();
        if (overFlow > 0 && targetView.getVisibility() != GONE && oldWidth > minimumWidth) {
            // we're still too big
            int newSize = Math.max(minimumWidth, oldWidth - overFlow);
            int childWidthSpec = MeasureSpec.makeMeasureSpec(newSize, MeasureSpec.AT_MOST);
            targetView.measure(childWidthSpec, heightSpec);
            overFlow -= oldWidth - newSize;
        }
        return overFlow;
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int left = getPaddingStart();
        int end = getMeasuredWidth();
        int childCount = getChildCount();
        int ownHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            if (child.getVisibility() == GONE) {
                continue;
            }
            int childHeight = child.getMeasuredHeight();
            MarginLayoutParams params = (MarginLayoutParams) child.getLayoutParams();
            int layoutLeft;
            int layoutRight;
            int top = (int) (getPaddingTop() + (ownHeight - childHeight) / 2.0f);
            int bottom = top + childHeight;
            // Icons that should go at the end
            if (mIconsAtEnd.contains(child)) {
                if (end == getMeasuredWidth()) {
                    layoutRight = end - mContentEndMargin;
                } else {
                    layoutRight = end - params.getMarginEnd();
                }
                layoutLeft = layoutRight - child.getMeasuredWidth();
                end = layoutLeft - params.getMarginStart();
            } else {
                left += params.getMarginStart();
                int right = left + child.getMeasuredWidth();
                layoutLeft = left;
                layoutRight = right;
                left = right + params.getMarginEnd();
            }
            if (getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
                int ltrLeft = layoutLeft;
                layoutLeft = getWidth() - layoutRight;
                layoutRight = getWidth() - ltrLeft;
            }
            child.layout(layoutLeft, top, layoutRight, bottom);
        }
        updateTouchListener();
    }

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(), attrs);
    }

    private void updateTouchListener() {
        if (mFeedbackListener == null) {
            setOnTouchListener(null);
            return;
        }
        setOnTouchListener(mTouchListener);
        mTouchListener.bindTouchRects();
    }

    /**
     * Sets onclick listener for feedback icon.
     */
    public void setFeedbackOnClickListener(OnClickListener l) {
        mFeedbackListener = l;
        mFeedbackIcon.setOnClickListener(mFeedbackListener);
        updateTouchListener();
    }

    /**
     * Sets the margin end for the text portion of the header, excluding right-aligned elements
     *
     * @param headerTextMarginEnd margin size
     */
    public void setHeaderTextMarginEnd(int headerTextMarginEnd) {
        if (mHeaderTextMarginEnd != headerTextMarginEnd) {
            mHeaderTextMarginEnd = headerTextMarginEnd;
            requestLayout();
        }
    }

    /**
     * Get the current margin end value for the header text
     *
     * @return margin size
     */
    public int getHeaderTextMarginEnd() {
        return mHeaderTextMarginEnd;
    }

    private class HeaderTouchListener implements OnTouchListener {

        private Rect mFeedbackRect;
        private int mTouchSlop;
        private boolean mTrackGesture;
        private float mDownX;
        private float mDownY;

        HeaderTouchListener() {
        }

        public void bindTouchRects() {
            mFeedbackRect = getRectAroundView(mFeedbackIcon);
            mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
        }

        private Rect getRectAroundView(View view) {
            float size = 48 * getResources().getDisplayMetrics().density;
            float width = Math.max(size, view.getWidth());
            float height = Math.max(size, view.getHeight());
            final Rect r = new Rect();
            if (view.getVisibility() == GONE) {
                view = getFirstChildNotGone();
                r.left = (int) (view.getLeft() - width / 2.0f);
            } else {
                r.left = (int) ((view.getLeft() + view.getRight()) / 2.0f - width / 2.0f);
            }
            r.top = (int) ((view.getTop() + view.getBottom()) / 2.0f - height / 2.0f);
            r.bottom = (int) (r.top + height);
            r.right = (int) (r.left + width);
            return r;
        }

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            float x = event.getX();
            float y = event.getY();
            switch (event.getActionMasked() & MotionEvent.ACTION_MASK) {
                case MotionEvent.ACTION_DOWN:
                    mTrackGesture = false;
                    if (isInside(x, y)) {
                        mDownX = x;
                        mDownY = y;
                        mTrackGesture = true;
                        return true;
                    }
                    break;
                case MotionEvent.ACTION_MOVE:
                    if (mTrackGesture) {
                        if (Math.abs(mDownX - x) > mTouchSlop
                                || Math.abs(mDownY - y) > mTouchSlop) {
                            mTrackGesture = false;
                        }
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    if (mTrackGesture && onTouchUp(x, y, mDownX, mDownY)) {
                        return true;
                    }
                    break;
            }
            return mTrackGesture;
        }

        private boolean onTouchUp(float upX, float upY, float downX, float downY) {
            if (mFeedbackIcon.isVisibleToUser()
                    && (mFeedbackRect.contains((int) upX, (int) upY)
                    || mFeedbackRect.contains((int) downX, (int) downY))) {
                mFeedbackIcon.performClick();
                return true;
            }
            return false;
        }

        private boolean isInside(float x, float y) {
            return mFeedbackRect.contains((int) x, (int) y);
        }
    }

    private View getFirstChildNotGone() {
        for (int i = 0; i < getChildCount(); i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                return child;
            }
        }
        return this;
    }

    @Override
    public boolean hasOverlappingRendering() {
        return false;
    }

    /**
     * Determine if the given point is touching an active part of the top line.
     */
    public boolean isInTouchRect(float x, float y) {
        if (mFeedbackListener == null) {
            return false;
        }
        return mTouchListener.isInside(x, y);
    }

    /**
     * Perform a click on an active part of the top line, if touching.
     */
    public boolean onTouchUp(float upX, float upY, float downX, float downY) {
        if (mFeedbackListener == null) {
            return false;
        }
        return mTouchListener.onTouchUp(upX, upY, downX, downY);
    }
}
+1 −112

File changed.

Preview size limit exceeded, changes collapsed.

+139 −0

File added.

Preview size limit exceeded, changes collapsed.

+3 −0
Original line number Diff line number Diff line
@@ -207,6 +207,9 @@
    <!-- Default padding for dialogs. -->
    <dimen name="dialog_padding">16dp</dimen>

    <!-- The horizontal margin of the content in the notification shade -->
    <dimen name="notification_shade_content_margin_horizontal">16dp</dimen>

    <!-- The margin on the start of the content view -->
    <dimen name="notification_content_margin_start">16dp</dimen>

Loading