1 /* 2 * Copyright (C) 2018 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.car.notification.template; 18 19 import android.annotation.ColorInt; 20 import android.annotation.Nullable; 21 import android.content.Context; 22 import android.content.res.TypedArray; 23 import android.graphics.drawable.Drawable; 24 import android.graphics.drawable.Icon; 25 import android.os.Handler; 26 import android.os.Looper; 27 import android.text.TextUtils; 28 import android.util.AttributeSet; 29 import android.view.View; 30 import android.widget.DateTimeView; 31 import android.widget.ImageView; 32 import android.widget.RelativeLayout; 33 import android.widget.TextView; 34 35 import androidx.annotation.VisibleForTesting; 36 37 import com.android.car.notification.NotificationUtils; 38 import com.android.car.notification.R; 39 40 /** 41 * Common notification body that consists of a title line, a content text line, and an image icon on 42 * the end. 43 * 44 * <p> For example, for a messaging notification, the title is the sender's name, 45 * the content is the message, and the image icon is the sender's avatar. 46 */ 47 public class CarNotificationBodyView extends RelativeLayout { 48 private static final int DEFAULT_MAX_LINES = 3; 49 @ColorInt 50 private final int mDefaultPrimaryTextColor; 51 @ColorInt 52 private final int mDefaultSecondaryTextColor; 53 private final boolean mUseLauncherIcon; 54 55 private boolean mIsHeadsUp; 56 private boolean mShowBigIcon; 57 private int mMaxLines; 58 @Nullable 59 private TextView mTitleView; 60 @Nullable 61 private TextView mContentView; 62 @Nullable 63 private ImageView mLargeIconView; 64 @Nullable 65 private TextView mCountView; 66 @Nullable 67 private DateTimeView mTimeView; 68 @Nullable 69 private ImageView mTitleIconView; 70 CarNotificationBodyView(Context context)71 public CarNotificationBodyView(Context context) { 72 super(context); 73 } 74 CarNotificationBodyView(Context context, AttributeSet attrs)75 public CarNotificationBodyView(Context context, AttributeSet attrs) { 76 super(context, attrs); 77 init(attrs); 78 } 79 CarNotificationBodyView(Context context, AttributeSet attrs, int defStyleAttr)80 public CarNotificationBodyView(Context context, AttributeSet attrs, int defStyleAttr) { 81 super(context, attrs, defStyleAttr); 82 init(attrs); 83 } 84 CarNotificationBodyView( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)85 public CarNotificationBodyView( 86 Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 87 super(context, attrs, defStyleAttr, defStyleRes); 88 init(attrs); 89 } 90 91 { 92 mDefaultPrimaryTextColor = 93 NotificationUtils.getAttrColor(getContext(), android.R.attr.textColorPrimary); 94 mDefaultSecondaryTextColor = 95 NotificationUtils.getAttrColor(getContext(), android.R.attr.textColorSecondary); 96 mUseLauncherIcon = getResources().getBoolean(R.bool.config_useLauncherIcon); 97 } 98 init(AttributeSet attrs)99 private void init(AttributeSet attrs) { 100 TypedArray attributes = 101 getContext().obtainStyledAttributes(attrs, R.styleable.CarNotificationBodyView); 102 mShowBigIcon = attributes.getBoolean(R.styleable.CarNotificationBodyView_showBigIcon, 103 /* defValue= */ false); 104 mMaxLines = attributes.getInteger(R.styleable.CarNotificationBodyView_maxLines, 105 /* defValue= */ DEFAULT_MAX_LINES); 106 mIsHeadsUp = attributes.getBoolean(R.styleable.CarNotificationHeaderView_isHeadsUp, 107 /* defValue= */ false); 108 inflate(getContext(), mIsHeadsUp ? R.layout.car_headsup_notification_body_view 109 : R.layout.car_notification_body_view, /* root= */ this); 110 attributes.recycle(); 111 } 112 113 @Override onFinishInflate()114 protected void onFinishInflate() { 115 super.onFinishInflate(); 116 mTitleView = findViewById(R.id.notification_body_title); 117 mTitleIconView = findViewById(R.id.notification_body_title_icon); 118 mContentView = findViewById(R.id.notification_body_content); 119 mLargeIconView = findViewById(R.id.notification_body_icon); 120 mCountView = findViewById(R.id.message_count); 121 mTimeView = findViewById(R.id.time); 122 if (mTimeView != null) { 123 mTimeView.setShowRelativeTime(true); 124 } 125 } 126 127 /** 128 * Binds the notification body. 129 * 130 * @param title the primary text 131 * @param content the secondary text, if this is null then content view will be hidden 132 * @param launcherIcon the launcher icon drawable for notification's package. 133 * If this and largeIcon are null then large icon view will be hidden. 134 * @param largeIcon the large icon, usually used for avatars. 135 * If this and launcherIcon are null then large icon view will be hidden. 136 * @param countText text signifying the number of messages inside this notification 137 * @param when wall clock time in milliseconds for the notification 138 */ bind(CharSequence title, @Nullable CharSequence content, @Nullable Drawable launcherIcon, @Nullable Icon largeIcon, @Nullable Drawable titleIcon, @Nullable CharSequence countText, @Nullable Long when)139 public void bind(CharSequence title, @Nullable CharSequence content, 140 @Nullable Drawable launcherIcon, @Nullable Icon largeIcon, @Nullable Drawable titleIcon, 141 @Nullable CharSequence countText, @Nullable Long when) { 142 setVisibility(View.VISIBLE); 143 144 if (mLargeIconView != null && largeIcon != null && !mUseLauncherIcon && mShowBigIcon) { 145 largeIcon.loadDrawableAsync(getContext(), drawable -> { 146 mLargeIconView.setVisibility(View.VISIBLE); 147 mLargeIconView.setImageDrawable(drawable); 148 }, Handler.createAsync(Looper.myLooper())); 149 } else if (mLargeIconView != null && launcherIcon != null && mUseLauncherIcon) { 150 mLargeIconView.setVisibility(View.VISIBLE); 151 mLargeIconView.setImageDrawable(launcherIcon); 152 } else if (mLargeIconView != null) { 153 mLargeIconView.setVisibility(View.GONE); 154 } 155 156 if (mTitleView != null) { 157 mTitleView.setVisibility(View.VISIBLE); 158 mTitleView.setText(title); 159 } 160 161 if (mTitleIconView != null && titleIcon != null) { 162 mTitleIconView.setVisibility(View.VISIBLE); 163 mTitleIconView.setImageDrawable(titleIcon); 164 } 165 166 if (mContentView != null && !TextUtils.isEmpty(content)) { 167 mContentView.setVisibility(View.VISIBLE); 168 mContentView.setMaxLines(mMaxLines); 169 mContentView.setText(content); 170 } else if (mContentView != null) { 171 mContentView.setVisibility(View.GONE); 172 } 173 174 if (mTimeView != null && when != null && !mIsHeadsUp) { 175 mTimeView.setVisibility(View.VISIBLE); 176 mTimeView.setTime(when); 177 } else if (mTimeView != null) { 178 mTimeView.setVisibility(View.GONE); 179 } 180 181 if (mCountView != null && countText != null) { 182 mCountView.setVisibility(View.VISIBLE); 183 mCountView.setText(countText); 184 } else if (mCountView != null) { 185 mCountView.setVisibility(View.GONE); 186 } 187 } 188 189 /** 190 * Sets the secondary text color. 191 */ setSecondaryTextColor(@olorInt int color)192 public void setSecondaryTextColor(@ColorInt int color) { 193 if (mContentView != null) { 194 mContentView.setTextColor(color); 195 } 196 } 197 198 /** 199 * Sets max lines for the content view. 200 */ setContentMaxLines(int maxLines)201 public void setContentMaxLines(int maxLines) { 202 if (mContentView != null) { 203 mContentView.setMaxLines(maxLines); 204 } 205 } 206 207 /** 208 * Sets the primary text color. 209 */ setPrimaryTextColor(@olorInt int color)210 public void setPrimaryTextColor(@ColorInt int color) { 211 if (mTitleView != null) { 212 mTitleView.setTextColor(color); 213 } 214 } 215 216 /** 217 * Sets the text color for the count field. 218 */ setCountTextColor(@olorInt int color)219 public void setCountTextColor(@ColorInt int color) { 220 if (mCountView != null) { 221 mCountView.setTextColor(color); 222 } 223 } 224 225 /** 226 * Sets the {@link OnClickListener} for the count field. 227 */ setCountOnClickListener(@ullable OnClickListener listener)228 public void setCountOnClickListener(@Nullable OnClickListener listener) { 229 if (mCountView != null) { 230 mCountView.setOnClickListener(listener); 231 } 232 } 233 234 /** 235 * Sets the text color for the time field. 236 */ setTimeTextColor(@olorInt int color)237 public void setTimeTextColor(@ColorInt int color) { 238 if (mTimeView != null) { 239 mTimeView.setTextColor(color); 240 } 241 } 242 243 /** 244 * Resets the notification actions empty for recycling. 245 */ reset()246 public void reset() { 247 setVisibility(View.GONE); 248 if (mTitleView != null) { 249 mTitleView.setVisibility(View.GONE); 250 } 251 if (mTitleIconView != null) { 252 mTitleIconView.setVisibility(View.GONE); 253 } 254 if (mContentView != null) { 255 setContentMaxLines(mMaxLines); 256 mContentView.setVisibility(View.GONE); 257 } 258 if (mLargeIconView != null) { 259 mLargeIconView.setVisibility(View.GONE); 260 } 261 setPrimaryTextColor(mDefaultPrimaryTextColor); 262 setSecondaryTextColor(mDefaultSecondaryTextColor); 263 if (mTimeView != null) { 264 mTimeView.setVisibility(View.GONE); 265 mTimeView.setTime(0); 266 setTimeTextColor(mDefaultPrimaryTextColor); 267 } 268 269 if (mCountView != null) { 270 mCountView.setVisibility(View.GONE); 271 mCountView.setText(null); 272 mCountView.setTextColor(mDefaultPrimaryTextColor); 273 } 274 } 275 276 @VisibleForTesting getTitleView()277 TextView getTitleView() { 278 return mTitleView; 279 } 280 281 @VisibleForTesting getContentView()282 TextView getContentView() { 283 return mContentView; 284 } 285 286 @VisibleForTesting getCountView()287 TextView getCountView() { 288 return mCountView; 289 } 290 291 @VisibleForTesting getTimeView()292 DateTimeView getTimeView() { 293 return mTimeView; 294 } 295 } 296