/** If there are actions, shows the button related views, and adds a button for each action. */ private void addActionButtons(RemoteViews bigView) { // Remove the existing buttons in case an existing notification is being updated. bigView.removeAllViews(R.id.buttons); // Always set the visibility of the views associated with the action buttons. The current // visibility state is not known as perhaps an existing notification is being updated. int visibility = mActions.isEmpty() ? View.GONE : View.VISIBLE; bigView.setViewVisibility(R.id.button_divider, visibility); bigView.setViewVisibility(R.id.buttons, visibility); Resources resources = mContext.getResources(); DisplayMetrics metrics = resources.getDisplayMetrics(); for (Action action : mActions) { RemoteViews view = new RemoteViews(mContext.getPackageName(), R.layout.web_notification_button); // If there is an icon then set it and add some padding. if (action.iconBitmap != null || action.iconId != 0) { if (useMaterial()) { view.setInt(R.id.button_icon, "setColorFilter", BUTTON_ICON_COLOR_MATERIAL); } int iconWidth = 0; if (action.iconBitmap != null) { view.setImageViewBitmap(R.id.button_icon, action.iconBitmap); iconWidth = action.iconBitmap.getWidth(); } else if (action.iconId != 0) { view.setImageViewResource(R.id.button_icon, action.iconId); BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(resources, action.iconId, options); iconWidth = options.outWidth; } iconWidth = dpToPx(Math.min(pxToDp(iconWidth, metrics), MAX_ACTION_ICON_WIDTH_DP), metrics); // Set the padding of the button so the text does not overlap with the icon. Flip // between left and right manually as RemoteViews does not expose a method that sets // padding in a writing-direction independent way. int buttonPadding = dpToPx(BUTTON_PADDING_START_DP + BUTTON_ICON_PADDING_DP, metrics) + iconWidth; int buttonPaddingLeft = LocalizationUtils.isLayoutRtl() ? 0 : buttonPadding; int buttonPaddingRight = LocalizationUtils.isLayoutRtl() ? buttonPadding : 0; view.setViewPadding(R.id.button, buttonPaddingLeft, 0, buttonPaddingRight, 0); } view.setTextViewText(R.id.button, action.title); view.setOnClickPendingIntent(R.id.button, action.intent); bigView.addView(R.id.buttons, view); } }
@Override public void setViewPadding(int viewId, int left, int top, int right, int bottom) { mRemoteViews.setViewPadding(viewId, left, top, right, bottom); }
@Override public Notification build() { // A note about RemoteViews and updating notifications. When a notification is passed to the // {@code NotificationManager} with the same tag and id as a previous notification, an // in-place update will be performed. In that case, the actions of all new // {@link RemoteViews} will be applied to the views of the old notification. This is safe // for actions that overwrite old values such as setting the text of a {@code TextView}, but // care must be taken for additive actions. Especially in the case of // {@link RemoteViews#addView} the result could be to append new views below stale ones. In // that case {@link RemoteViews#removeAllViews} must be called before adding new ones. RemoteViews compactView = new RemoteViews(mContext.getPackageName(), R.layout.web_notification); RemoteViews bigView = new RemoteViews(mContext.getPackageName(), R.layout.web_notification_big); float fontScale = mContext.getResources().getConfiguration().fontScale; bigView.setInt(R.id.body, "setMaxLines", calculateMaxBodyLines(fontScale)); int scaledPadding = calculateScaledPadding(fontScale, mContext.getResources().getDisplayMetrics()); String formattedTime = ""; // Temporarily allowing disk access. TODO: Fix. See http://crbug.com/577185 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); StrictMode.allowThreadDiskWrites(); try { long time = SystemClock.elapsedRealtime(); formattedTime = DateFormat.getTimeFormat(mContext).format(new Date()); RecordHistogram.recordTimesHistogram( "Android.StrictMode.NotificationUIBuildTime", SystemClock.elapsedRealtime() - time, TimeUnit.MILLISECONDS); } finally { StrictMode.setThreadPolicy(oldPolicy); } for (RemoteViews view : new RemoteViews[] {compactView, bigView}) { view.setTextViewText(R.id.time, formattedTime); view.setTextViewText(R.id.title, mTitle); view.setTextViewText(R.id.body, mBody); view.setTextViewText(R.id.origin, mOrigin); view.setImageViewBitmap(R.id.icon, getNormalizedLargeIcon()); view.setViewPadding(R.id.title, 0, scaledPadding, 0, 0); view.setViewPadding(R.id.body_container, 0, scaledPadding, 0, scaledPadding); addWorkProfileBadge(view); int smallIconId = useMaterial() ? R.id.small_icon_overlay : R.id.small_icon_footer; view.setViewVisibility(smallIconId, View.VISIBLE); if (mSmallIconBitmap != null) { view.setImageViewBitmap(smallIconId, mSmallIconBitmap); } else { view.setImageViewResource(smallIconId, mSmallIconId); } } addActionButtons(bigView); configureSettingsButton(bigView); // Note: this is not a NotificationCompat builder so be mindful of the // API level of methods you call on the builder. Notification.Builder builder = new Notification.Builder(mContext); builder.setTicker(mTickerText); builder.setContentIntent(mContentIntent); builder.setDeleteIntent(mDeleteIntent); builder.setDefaults(mDefaults); builder.setVibrate(mVibratePattern); builder.setWhen(mTimestamp); builder.setOnlyAlertOnce(!mRenotify); builder.setContent(compactView); // Some things are duplicated in the builder to ensure the notification shows correctly on // Wear devices and custom lock screens. builder.setContentTitle(mTitle); builder.setContentText(mBody); builder.setSubText(mOrigin); builder.setLargeIcon(getNormalizedLargeIcon()); setSmallIconOnBuilder(builder, mSmallIconId, mSmallIconBitmap); for (Action action : mActions) { addActionToBuilder(builder, action); } if (mSettingsAction != null) { addActionToBuilder(builder, mSettingsAction); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Notification.Builder.setPublicVersion was added in Android L. builder.setPublicVersion(createPublicNotification(mContext)); } Notification notification = builder.build(); notification.bigContentView = bigView; return notification; }