summaryrefslogtreecommitdiff
path: root/android/view/NotificationHeaderView.java
diff options
context:
space:
mode:
authorJustin Klaassen <justinklaassen@google.com>2017-09-15 17:58:39 -0400
committerJustin Klaassen <justinklaassen@google.com>2017-09-15 17:58:39 -0400
commit10d07c88d69cc64f73a069163e7ea5ba2519a099 (patch)
tree8dbd149eb350320a29c3d10e7ad3201de1c5cbee /android/view/NotificationHeaderView.java
parent677516fb6b6f207d373984757d3d9450474b6b00 (diff)
downloadandroid-28-10d07c88d69cc64f73a069163e7ea5ba2519a099.tar.gz
Import Android SDK Platform PI [4335822]
/google/data/ro/projects/android/fetch_artifact \ --bid 4335822 \ --target sdk_phone_armv7-win_sdk \ sdk-repo-linux-sources-4335822.zip AndroidVersion.ApiLevel has been modified to appear as 28 Change-Id: Ic8f04be005a71c2b9abeaac754d8da8d6f9a2c32
Diffstat (limited to 'android/view/NotificationHeaderView.java')
-rw-r--r--android/view/NotificationHeaderView.java457
1 files changed, 457 insertions, 0 deletions
diff --git a/android/view/NotificationHeaderView.java b/android/view/NotificationHeaderView.java
new file mode 100644
index 00000000..58045602
--- /dev/null
+++ b/android/view/NotificationHeaderView.java
@@ -0,0 +1,457 @@
+/*
+ * 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.app.Notification;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Outline;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.RemoteViews;
+
+import com.android.internal.R;
+import com.android.internal.widget.CachingIconView;
+
+import java.util.ArrayList;
+
+/**
+ * A header of a notification view
+ *
+ * @hide
+ */
+@RemoteViews.RemoteView
+public class NotificationHeaderView extends ViewGroup {
+ public static final int NO_COLOR = Notification.COLOR_INVALID;
+ private final int mChildMinWidth;
+ private final int mContentEndMargin;
+ private View mAppName;
+ private View mHeaderText;
+ private OnClickListener mExpandClickListener;
+ private HeaderTouchListener mTouchListener = new HeaderTouchListener();
+ private ImageView mExpandButton;
+ private CachingIconView mIcon;
+ private View mProfileBadge;
+ private View mInfo;
+ private int mIconColor;
+ private int mOriginalNotificationColor;
+ private boolean mExpanded;
+ private boolean mShowExpandButtonAtEnd;
+ private boolean mShowWorkBadgeAtEnd;
+ private Drawable mBackground;
+ private int mHeaderBackgroundHeight;
+ private boolean mEntireHeaderClickable;
+ private boolean mExpandOnlyOnButton;
+ private boolean mAcceptAllTouches;
+
+ ViewOutlineProvider mProvider = new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ if (mBackground != null) {
+ outline.setRect(0, 0, getWidth(), mHeaderBackgroundHeight);
+ outline.setAlpha(1f);
+ }
+ }
+ };
+
+ public NotificationHeaderView(Context context) {
+ this(context, null);
+ }
+
+ public NotificationHeaderView(Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public NotificationHeaderView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public NotificationHeaderView(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);
+ mHeaderBackgroundHeight = res.getDimensionPixelSize(
+ R.dimen.notification_header_background_height);
+ mEntireHeaderClickable = res.getBoolean(R.bool.config_notificationHeaderClickableForExpand);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mAppName = findViewById(com.android.internal.R.id.app_name_text);
+ mHeaderText = findViewById(com.android.internal.R.id.header_text);
+ mExpandButton = findViewById(com.android.internal.R.id.expand_button);
+ mIcon = findViewById(com.android.internal.R.id.icon);
+ mProfileBadge = findViewById(com.android.internal.R.id.profile_badge);
+ }
+
+ @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() + 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);
+ totalWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth();
+ }
+ if (totalWidth > givenWidth) {
+ int overFlow = totalWidth - givenWidth;
+ // We are overflowing, lets shrink the app name first
+ final int appWidth = mAppName.getMeasuredWidth();
+ if (overFlow > 0 && mAppName.getVisibility() != GONE && appWidth > mChildMinWidth) {
+ int newSize = appWidth - Math.min(appWidth - mChildMinWidth, overFlow);
+ int childWidthSpec = MeasureSpec.makeMeasureSpec(newSize, MeasureSpec.AT_MOST);
+ mAppName.measure(childWidthSpec, wrapContentHeightSpec);
+ overFlow -= appWidth - newSize;
+ }
+ // still overflowing, finaly we shrink the header text
+ if (overFlow > 0 && mHeaderText.getVisibility() != GONE) {
+ // we're still too big
+ final int textWidth = mHeaderText.getMeasuredWidth();
+ int newSize = Math.max(0, textWidth - overFlow);
+ int childWidthSpec = MeasureSpec.makeMeasureSpec(newSize, MeasureSpec.AT_MOST);
+ mHeaderText.measure(childWidthSpec, wrapContentHeightSpec);
+ }
+ }
+ setMeasuredDimension(givenWidth, givenHeight);
+ }
+
+ @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();
+ left += params.getMarginStart();
+ int right = left + child.getMeasuredWidth();
+ int top = (int) (getPaddingTop() + (ownHeight - childHeight) / 2.0f);
+ int bottom = top + childHeight;
+ int layoutLeft = left;
+ int layoutRight = right;
+ if (child == mExpandButton && mShowExpandButtonAtEnd) {
+ layoutRight = end - mContentEndMargin;
+ end = layoutLeft = layoutRight - child.getMeasuredWidth();
+ }
+ if (child == mProfileBadge) {
+ int paddingEnd = getPaddingEnd();
+ if (mShowWorkBadgeAtEnd) {
+ paddingEnd = mContentEndMargin;
+ }
+ layoutRight = end - paddingEnd;
+ end = layoutLeft = layoutRight - child.getMeasuredWidth();
+ }
+ if (getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
+ int ltrLeft = layoutLeft;
+ layoutLeft = getWidth() - layoutRight;
+ layoutRight = getWidth() - ltrLeft;
+ }
+ child.layout(layoutLeft, top, layoutRight, bottom);
+ left = right + params.getMarginEnd();
+ }
+ updateTouchListener();
+ }
+
+ @Override
+ public LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new ViewGroup.MarginLayoutParams(getContext(), attrs);
+ }
+
+ /**
+ * Set a {@link Drawable} to be displayed as a background on the header.
+ */
+ public void setHeaderBackgroundDrawable(Drawable drawable) {
+ if (drawable != null) {
+ setWillNotDraw(false);
+ mBackground = drawable;
+ mBackground.setCallback(this);
+ setOutlineProvider(mProvider);
+ } else {
+ setWillNotDraw(true);
+ mBackground = null;
+ setOutlineProvider(null);
+ }
+ invalidate();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mBackground != null) {
+ mBackground.setBounds(0, 0, getWidth(), mHeaderBackgroundHeight);
+ mBackground.draw(canvas);
+ }
+ }
+
+ @Override
+ protected boolean verifyDrawable(Drawable who) {
+ return super.verifyDrawable(who) || who == mBackground;
+ }
+
+ @Override
+ protected void drawableStateChanged() {
+ if (mBackground != null && mBackground.isStateful()) {
+ mBackground.setState(getDrawableState());
+ }
+ }
+
+ private void updateTouchListener() {
+ if (mExpandClickListener != null) {
+ mTouchListener.bindTouchRects();
+ }
+ }
+
+ @Override
+ public void setOnClickListener(@Nullable OnClickListener l) {
+ mExpandClickListener = l;
+ setOnTouchListener(mExpandClickListener != null ? mTouchListener : null);
+ mExpandButton.setOnClickListener(mExpandClickListener);
+ updateTouchListener();
+ }
+
+ @RemotableViewMethod
+ public void setOriginalIconColor(int color) {
+ mIconColor = color;
+ }
+
+ public int getOriginalIconColor() {
+ return mIconColor;
+ }
+
+ @RemotableViewMethod
+ public void setOriginalNotificationColor(int color) {
+ mOriginalNotificationColor = color;
+ }
+
+ public int getOriginalNotificationColor() {
+ return mOriginalNotificationColor;
+ }
+
+ @RemotableViewMethod
+ public void setExpanded(boolean expanded) {
+ mExpanded = expanded;
+ updateExpandButton();
+ }
+
+ private void updateExpandButton() {
+ int drawableId;
+ int contentDescriptionId;
+ if (mExpanded) {
+ drawableId = R.drawable.ic_collapse_notification;
+ contentDescriptionId = R.string.expand_button_content_description_expanded;
+ } else {
+ drawableId = R.drawable.ic_expand_notification;
+ contentDescriptionId = R.string.expand_button_content_description_collapsed;
+ }
+ mExpandButton.setImageDrawable(getContext().getDrawable(drawableId));
+ mExpandButton.setColorFilter(mOriginalNotificationColor);
+ mExpandButton.setContentDescription(mContext.getText(contentDescriptionId));
+ }
+
+ public void setShowWorkBadgeAtEnd(boolean showWorkBadgeAtEnd) {
+ if (showWorkBadgeAtEnd != mShowWorkBadgeAtEnd) {
+ setClipToPadding(!showWorkBadgeAtEnd);
+ mShowWorkBadgeAtEnd = showWorkBadgeAtEnd;
+ }
+ }
+
+ /**
+ * Sets whether or not the expand button appears at the end of the NotificationHeaderView. If
+ * both this and {@link #setShowWorkBadgeAtEnd(boolean)} have been set to true, then the
+ * expand button will appear closer to the end than the work badge.
+ */
+ public void setShowExpandButtonAtEnd(boolean showExpandButtonAtEnd) {
+ if (showExpandButtonAtEnd != mShowExpandButtonAtEnd) {
+ setClipToPadding(!showExpandButtonAtEnd);
+ mShowExpandButtonAtEnd = showExpandButtonAtEnd;
+ }
+ }
+
+ public View getWorkProfileIcon() {
+ return mProfileBadge;
+ }
+
+ public CachingIconView getIcon() {
+ return mIcon;
+ }
+
+ public class HeaderTouchListener implements View.OnTouchListener {
+
+ private final ArrayList<Rect> mTouchRects = new ArrayList<>();
+ private Rect mExpandButtonRect;
+ private int mTouchSlop;
+ private boolean mTrackGesture;
+ private float mDownX;
+ private float mDownY;
+
+ public HeaderTouchListener() {
+ }
+
+ public void bindTouchRects() {
+ mTouchRects.clear();
+ addRectAroundView(mIcon);
+ mExpandButtonRect = addRectAroundView(mExpandButton);
+ addWidthRect();
+ mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
+ }
+
+ private void addWidthRect() {
+ Rect r = new Rect();
+ r.top = 0;
+ r.bottom = (int) (32 * getResources().getDisplayMetrics().density);
+ r.left = 0;
+ r.right = getWidth();
+ mTouchRects.add(r);
+ }
+
+ private Rect addRectAroundView(View view) {
+ final Rect r = getRectAroundView(view);
+ mTouchRects.add(r);
+ return r;
+ }
+
+ private Rect getRectAroundView(View view) {
+ float size = 48 * getResources().getDisplayMetrics().density;
+ final Rect r = new Rect();
+ if (view.getVisibility() == GONE) {
+ view = getFirstChildNotGone();
+ r.left = (int) (view.getLeft() - size / 2.0f);
+ } else {
+ r.left = (int) ((view.getLeft() + view.getRight()) / 2.0f - size / 2.0f);
+ }
+ r.top = (int) ((view.getTop() + view.getBottom()) / 2.0f - size / 2.0f);
+ r.bottom = (int) (r.top + size);
+ r.right = (int) (r.left + size);
+ 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) {
+ mExpandButton.performClick();
+ }
+ break;
+ }
+ return mTrackGesture;
+ }
+
+ private boolean isInside(float x, float y) {
+ if (mAcceptAllTouches) {
+ return true;
+ }
+ if (mExpandOnlyOnButton) {
+ return mExpandButtonRect.contains((int) x, (int) y);
+ }
+ for (int i = 0; i < mTouchRects.size(); i++) {
+ Rect r = mTouchRects.get(i);
+ if (r.contains((int) x, (int) y)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ private View getFirstChildNotGone() {
+ for (int i = 0; i < getChildCount(); i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() != GONE) {
+ return child;
+ }
+ }
+ return this;
+ }
+
+ public ImageView getExpandButton() {
+ return mExpandButton;
+ }
+
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
+
+ public boolean isInTouchRect(float x, float y) {
+ if (mExpandClickListener == null) {
+ return false;
+ }
+ return mTouchListener.isInside(x, y);
+ }
+
+ /**
+ * Sets whether or not all touches to this header view will register as a click. Note that
+ * if the config value for {@code config_notificationHeaderClickableForExpand} is {@code true},
+ * then calling this method with {@code false} will not override that configuration.
+ */
+ @RemotableViewMethod
+ public void setAcceptAllTouches(boolean acceptAllTouches) {
+ mAcceptAllTouches = mEntireHeaderClickable || acceptAllTouches;
+ }
+
+ /**
+ * Sets whether only the expand icon itself should serve as the expand target.
+ */
+ @RemotableViewMethod
+ public void setExpandOnlyOnButton(boolean expandOnlyOnButton) {
+ mExpandOnlyOnButton = expandOnlyOnButton;
+ }
+}