1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.internal.widget; 18 19 import android.annotation.ColorInt; 20 import android.annotation.Nullable; 21 import android.content.Context; 22 import android.content.res.ColorStateList; 23 import android.graphics.Rect; 24 import android.util.AttributeSet; 25 import android.view.RemotableViewMethod; 26 import android.view.View; 27 import android.view.ViewGroup; 28 import android.view.accessibility.AccessibilityNodeInfo; 29 import android.widget.Button; 30 import android.widget.FrameLayout; 31 import android.widget.ImageView; 32 import android.widget.RemoteViews; 33 import android.widget.TextView; 34 35 import com.android.internal.R; 36 37 import java.util.Locale; 38 39 /** 40 * An expand button in a notification 41 */ 42 @RemoteViews.RemoteView 43 public class NotificationExpandButton extends FrameLayout { 44 45 private View mPillView; 46 private TextView mNumberView; 47 private ImageView mIconView; 48 private boolean mExpanded; 49 private int mNumber; 50 private int mDefaultPillColor; 51 private int mDefaultTextColor; 52 private int mHighlightPillColor; 53 private int mHighlightTextColor; 54 NotificationExpandButton(Context context)55 public NotificationExpandButton(Context context) { 56 this(context, null, 0, 0); 57 } 58 NotificationExpandButton(Context context, @Nullable AttributeSet attrs)59 public NotificationExpandButton(Context context, @Nullable AttributeSet attrs) { 60 this(context, attrs, 0, 0); 61 } 62 NotificationExpandButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr)63 public NotificationExpandButton(Context context, @Nullable AttributeSet attrs, 64 int defStyleAttr) { 65 this(context, attrs, defStyleAttr, 0); 66 } 67 NotificationExpandButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes)68 public NotificationExpandButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr, 69 int defStyleRes) { 70 super(context, attrs, defStyleAttr, defStyleRes); 71 } 72 73 @Override onFinishInflate()74 protected void onFinishInflate() { 75 super.onFinishInflate(); 76 mPillView = findViewById(R.id.expand_button_pill); 77 mNumberView = findViewById(R.id.expand_button_number); 78 mIconView = findViewById(R.id.expand_button_icon); 79 } 80 81 /** 82 * Show the touchable area of the view for a11y. 83 * If the parent is the touch container, then that view's bounds are the touchable area. 84 */ 85 @Override getBoundsOnScreen(Rect outRect, boolean clipToParent)86 public void getBoundsOnScreen(Rect outRect, boolean clipToParent) { 87 ViewGroup parent = (ViewGroup) getParent(); 88 if (parent != null && parent.getId() == R.id.expand_button_touch_container) { 89 parent.getBoundsOnScreen(outRect, clipToParent); 90 } else { 91 super.getBoundsOnScreen(outRect, clipToParent); 92 } 93 } 94 95 /** 96 * Determined if the given point should be touchable. 97 * If the parent is the touch container, then any point in that view should be touchable. 98 */ 99 @Override pointInView(float localX, float localY, float slop)100 public boolean pointInView(float localX, float localY, float slop) { 101 ViewGroup parent = (ViewGroup) getParent(); 102 if (parent != null && parent.getId() == R.id.expand_button_touch_container) { 103 // If our parent is checking with us, then the point must be within its bounds. 104 return true; 105 } 106 return super.pointInView(localX, localY, slop); 107 } 108 109 @Override onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info)110 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { 111 super.onInitializeAccessibilityNodeInfo(info); 112 info.setClassName(Button.class.getName()); 113 } 114 115 /** 116 * Update the button's drawable, content description, and color for the given expanded state. 117 */ 118 @RemotableViewMethod setExpanded(boolean expanded)119 public void setExpanded(boolean expanded) { 120 mExpanded = expanded; 121 updateExpandedState(); 122 } 123 updateExpandedState()124 private void updateExpandedState() { 125 int drawableId; 126 int contentDescriptionId; 127 if (mExpanded) { 128 drawableId = R.drawable.ic_collapse_notification; 129 contentDescriptionId = R.string.expand_button_content_description_expanded; 130 } else { 131 drawableId = R.drawable.ic_expand_notification; 132 contentDescriptionId = R.string.expand_button_content_description_collapsed; 133 } 134 setContentDescription(mContext.getText(contentDescriptionId)); 135 mIconView.setImageDrawable(getContext().getDrawable(drawableId)); 136 137 // changing the expanded state can affect the number display 138 updateNumber(); 139 } 140 updateNumber()141 private void updateNumber() { 142 if (shouldShowNumber()) { 143 CharSequence text = mNumber >= 100 144 ? getResources().getString(R.string.unread_convo_overflow, 99) 145 : String.format(Locale.getDefault(), "%d", mNumber); 146 mNumberView.setText(text); 147 mNumberView.setVisibility(VISIBLE); 148 } else { 149 mNumberView.setVisibility(GONE); 150 } 151 152 // changing number can affect the color 153 updateColors(); 154 } 155 updateColors()156 private void updateColors() { 157 if (shouldShowNumber()) { 158 if (mHighlightPillColor != 0) { 159 mPillView.setBackgroundTintList(ColorStateList.valueOf(mHighlightPillColor)); 160 } 161 mIconView.setColorFilter(mHighlightTextColor); 162 if (mHighlightTextColor != 0) { 163 mNumberView.setTextColor(mHighlightTextColor); 164 } 165 } else { 166 if (mDefaultPillColor != 0) { 167 mPillView.setBackgroundTintList(ColorStateList.valueOf(mDefaultPillColor)); 168 } 169 mIconView.setColorFilter(mDefaultTextColor); 170 if (mDefaultTextColor != 0) { 171 mNumberView.setTextColor(mDefaultTextColor); 172 } 173 } 174 } 175 shouldShowNumber()176 private boolean shouldShowNumber() { 177 return !mExpanded && mNumber > 1; 178 } 179 180 /** 181 * Set the color used for the expand chevron and the text 182 */ 183 @RemotableViewMethod setDefaultTextColor(int color)184 public void setDefaultTextColor(int color) { 185 mDefaultTextColor = color; 186 updateColors(); 187 } 188 189 /** 190 * Sets the color used to for the expander when there is no number shown 191 */ 192 @RemotableViewMethod setDefaultPillColor(@olorInt int color)193 public void setDefaultPillColor(@ColorInt int color) { 194 mDefaultPillColor = color; 195 updateColors(); 196 } 197 198 /** 199 * Set the color used for the expand chevron and the text 200 */ 201 @RemotableViewMethod setHighlightTextColor(int color)202 public void setHighlightTextColor(int color) { 203 mHighlightTextColor = color; 204 updateColors(); 205 } 206 207 /** 208 * Sets the color used to highlight the expander when there is a number shown 209 */ 210 @RemotableViewMethod setHighlightPillColor(@olorInt int color)211 public void setHighlightPillColor(@ColorInt int color) { 212 mHighlightPillColor = color; 213 updateColors(); 214 } 215 216 /** 217 * Sets the number shown inside the expand button. 218 * This only appears when the expand button is collapsed, and when greater than 1. 219 */ 220 @RemotableViewMethod setNumber(int number)221 public void setNumber(int number) { 222 if (mNumber != number) { 223 mNumber = number; 224 updateNumber(); 225 } 226 } 227 } 228