Mady Mellor | c3d6f7d | 2018-11-07 09:36:56 -0800 | [diff] [blame] | 1 | /* |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 2 | * Copyright (C) 2018 The Android Open Source Project |
Mady Mellor | c3d6f7d | 2018-11-07 09:36:56 -0800 | [diff] [blame] | 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.systemui.bubbles; |
| 18 | |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 19 | import android.annotation.Nullable; |
Mady Mellor | c3d6f7d | 2018-11-07 09:36:56 -0800 | [diff] [blame] | 20 | import android.app.Notification; |
| 21 | import android.content.Context; |
Lyn Han | 4c1731f | 2019-06-19 19:14:24 -0700 | [diff] [blame] | 22 | import android.graphics.Bitmap; |
Mady Mellor | d20ff41 | 2019-07-31 17:42:52 -0700 | [diff] [blame] | 23 | import android.graphics.Canvas; |
Mady Mellor | c3d6f7d | 2018-11-07 09:36:56 -0800 | [diff] [blame] | 24 | import android.graphics.Color; |
Lyn Han | ecdd06e | 2019-07-10 18:19:37 -0700 | [diff] [blame] | 25 | import android.graphics.Matrix; |
Lyn Han | 0e82a3e | 2019-06-19 18:47:06 -0700 | [diff] [blame] | 26 | import android.graphics.Path; |
Lyn Han | 56a3ec5 | 2019-03-25 15:04:21 -0700 | [diff] [blame] | 27 | import android.graphics.drawable.AdaptiveIconDrawable; |
Mady Mellor | d20ff41 | 2019-07-31 17:42:52 -0700 | [diff] [blame] | 28 | import android.graphics.drawable.BitmapDrawable; |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 29 | import android.graphics.drawable.ColorDrawable; |
Mady Mellor | c3d6f7d | 2018-11-07 09:36:56 -0800 | [diff] [blame] | 30 | import android.graphics.drawable.Drawable; |
| 31 | import android.graphics.drawable.Icon; |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 32 | import android.graphics.drawable.InsetDrawable; |
Mady Mellor | c3d6f7d | 2018-11-07 09:36:56 -0800 | [diff] [blame] | 33 | import android.util.AttributeSet; |
Lyn Han | ecdd06e | 2019-07-10 18:19:37 -0700 | [diff] [blame] | 34 | import android.util.PathParser; |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 35 | import android.widget.FrameLayout; |
Mady Mellor | c3d6f7d | 2018-11-07 09:36:56 -0800 | [diff] [blame] | 36 | |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 37 | import com.android.internal.graphics.ColorUtils; |
Mady Mellor | d20ff41 | 2019-07-31 17:42:52 -0700 | [diff] [blame] | 38 | import com.android.launcher3.icons.ShadowGenerator; |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 39 | import com.android.systemui.Interpolators; |
Mady Mellor | c3d6f7d | 2018-11-07 09:36:56 -0800 | [diff] [blame] | 40 | import com.android.systemui.R; |
Ned Burns | f81c4c4 | 2019-01-07 14:10:43 -0500 | [diff] [blame] | 41 | import com.android.systemui.statusbar.notification.collection.NotificationEntry; |
Mady Mellor | c3d6f7d | 2018-11-07 09:36:56 -0800 | [diff] [blame] | 42 | import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; |
| 43 | |
| 44 | /** |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 45 | * A floating object on the screen that can post message updates. |
Mady Mellor | c3d6f7d | 2018-11-07 09:36:56 -0800 | [diff] [blame] | 46 | */ |
Joshua Tsuji | 442b627 | 2019-02-08 13:23:43 -0500 | [diff] [blame] | 47 | public class BubbleView extends FrameLayout { |
Mady Mellor | c3d6f7d | 2018-11-07 09:36:56 -0800 | [diff] [blame] | 48 | |
Lyn Han | 56a3ec5 | 2019-03-25 15:04:21 -0700 | [diff] [blame] | 49 | private static final int DARK_ICON_ALPHA = 180; |
| 50 | private static final double ICON_MIN_CONTRAST = 4.1; |
Lyn Han | 1b4f25e | 2019-06-11 13:56:34 -0700 | [diff] [blame] | 51 | private static final int DEFAULT_BACKGROUND_COLOR = Color.LTGRAY; |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 52 | // Same value as Launcher3 badge code |
| 53 | private static final float WHITE_SCRIM_ALPHA = 0.54f; |
Mady Mellor | c3d6f7d | 2018-11-07 09:36:56 -0800 | [diff] [blame] | 54 | private Context mContext; |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 55 | |
| 56 | private BadgedImageView mBadgedImageView; |
Joshua Tsuji | 6549e70 | 2019-05-02 13:13:16 -0400 | [diff] [blame] | 57 | private int mBadgeColor; |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 58 | private int mIconInset; |
Lyn Han | 4c1731f | 2019-06-19 19:14:24 -0700 | [diff] [blame] | 59 | private Drawable mUserBadgedAppIcon; |
Mady Mellor | c3d6f7d | 2018-11-07 09:36:56 -0800 | [diff] [blame] | 60 | |
Lyn Han | 1b4f25e | 2019-06-11 13:56:34 -0700 | [diff] [blame] | 61 | // mBubbleIconFactory cannot be static because it depends on Context. |
| 62 | private BubbleIconFactory mBubbleIconFactory; |
| 63 | |
Joshua Tsuji | 6549e70 | 2019-05-02 13:13:16 -0400 | [diff] [blame] | 64 | private boolean mSuppressDot = false; |
| 65 | |
Mady Mellor | 99a30260 | 2019-06-14 11:39:56 -0700 | [diff] [blame] | 66 | private Bubble mBubble; |
Mady Mellor | c3d6f7d | 2018-11-07 09:36:56 -0800 | [diff] [blame] | 67 | |
| 68 | public BubbleView(Context context) { |
| 69 | this(context, null); |
| 70 | } |
| 71 | |
| 72 | public BubbleView(Context context, AttributeSet attrs) { |
| 73 | this(context, attrs, 0); |
| 74 | } |
| 75 | |
| 76 | public BubbleView(Context context, AttributeSet attrs, int defStyleAttr) { |
| 77 | this(context, attrs, defStyleAttr, 0); |
| 78 | } |
| 79 | |
| 80 | public BubbleView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { |
| 81 | super(context, attrs, defStyleAttr, defStyleRes); |
Mady Mellor | c3d6f7d | 2018-11-07 09:36:56 -0800 | [diff] [blame] | 82 | mContext = context; |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 83 | mIconInset = getResources().getDimensionPixelSize(R.dimen.bubble_icon_inset); |
| 84 | } |
| 85 | |
| 86 | @Override |
| 87 | protected void onFinishInflate() { |
| 88 | super.onFinishInflate(); |
Joshua Tsuji | 614b1df | 2019-03-26 13:57:05 -0400 | [diff] [blame] | 89 | mBadgedImageView = findViewById(R.id.bubble_image); |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 90 | } |
| 91 | |
| 92 | @Override |
| 93 | protected void onAttachedToWindow() { |
| 94 | super.onAttachedToWindow(); |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 95 | } |
| 96 | |
Mady Mellor | c3d6f7d | 2018-11-07 09:36:56 -0800 | [diff] [blame] | 97 | /** |
Mady Mellor | 99a30260 | 2019-06-14 11:39:56 -0700 | [diff] [blame] | 98 | * Populates this view with a bubble. |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 99 | * <p> |
Mady Mellor | 99a30260 | 2019-06-14 11:39:56 -0700 | [diff] [blame] | 100 | * This should only be called when a new bubble is being set on the view, updates to the |
| 101 | * current bubble should use {@link #update(Bubble)}. |
Mady Mellor | c3d6f7d | 2018-11-07 09:36:56 -0800 | [diff] [blame] | 102 | * |
Mady Mellor | 99a30260 | 2019-06-14 11:39:56 -0700 | [diff] [blame] | 103 | * @param bubble the bubble to display in this view. |
Mady Mellor | c3d6f7d | 2018-11-07 09:36:56 -0800 | [diff] [blame] | 104 | */ |
Mady Mellor | 99a30260 | 2019-06-14 11:39:56 -0700 | [diff] [blame] | 105 | public void setBubble(Bubble bubble) { |
| 106 | mBubble = bubble; |
Mady Mellor | c3d6f7d | 2018-11-07 09:36:56 -0800 | [diff] [blame] | 107 | } |
| 108 | |
| 109 | /** |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 110 | * The {@link NotificationEntry} associated with this view, if one exists. |
Mady Mellor | c3d6f7d | 2018-11-07 09:36:56 -0800 | [diff] [blame] | 111 | */ |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 112 | @Nullable |
Ned Burns | f81c4c4 | 2019-01-07 14:10:43 -0500 | [diff] [blame] | 113 | public NotificationEntry getEntry() { |
Mady Mellor | 99a30260 | 2019-06-14 11:39:56 -0700 | [diff] [blame] | 114 | return mBubble != null ? mBubble.getEntry() : null; |
Mady Mellor | c3d6f7d | 2018-11-07 09:36:56 -0800 | [diff] [blame] | 115 | } |
| 116 | |
| 117 | /** |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 118 | * The key for the {@link NotificationEntry} associated with this view, if one exists. |
Mady Mellor | c3d6f7d | 2018-11-07 09:36:56 -0800 | [diff] [blame] | 119 | */ |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 120 | @Nullable |
| 121 | public String getKey() { |
Mady Mellor | 99a30260 | 2019-06-14 11:39:56 -0700 | [diff] [blame] | 122 | return (mBubble != null) ? mBubble.getKey() : null; |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 123 | } |
| 124 | |
| 125 | /** |
Mady Mellor | 99a30260 | 2019-06-14 11:39:56 -0700 | [diff] [blame] | 126 | * Updates the UI based on the bubble, updates badge and animates messages as needed. |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 127 | */ |
Mady Mellor | 99a30260 | 2019-06-14 11:39:56 -0700 | [diff] [blame] | 128 | public void update(Bubble bubble) { |
| 129 | mBubble = bubble; |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 130 | updateViews(); |
| 131 | } |
| 132 | |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 133 | /** |
Lyn Han | 1b4f25e | 2019-06-11 13:56:34 -0700 | [diff] [blame] | 134 | * @param factory Factory for creating normalized bubble icons. |
| 135 | */ |
| 136 | public void setBubbleIconFactory(BubbleIconFactory factory) { |
| 137 | mBubbleIconFactory = factory; |
| 138 | } |
| 139 | |
Lyn Han | 4c1731f | 2019-06-19 19:14:24 -0700 | [diff] [blame] | 140 | public void setAppIcon(Drawable appIcon) { |
| 141 | mUserBadgedAppIcon = appIcon; |
| 142 | } |
Lyn Han | 1b4f25e | 2019-06-11 13:56:34 -0700 | [diff] [blame] | 143 | /** |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 144 | * @return the {@link ExpandableNotificationRow} view to display notification content when the |
| 145 | * bubble is expanded. |
| 146 | */ |
| 147 | @Nullable |
Mady Mellor | c3d6f7d | 2018-11-07 09:36:56 -0800 | [diff] [blame] | 148 | public ExpandableNotificationRow getRowView() { |
Mady Mellor | 99a30260 | 2019-06-14 11:39:56 -0700 | [diff] [blame] | 149 | return (mBubble != null) ? mBubble.getEntry().getRow() : null; |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 150 | } |
| 151 | |
Joshua Tsuji | 6549e70 | 2019-05-02 13:13:16 -0400 | [diff] [blame] | 152 | /** Changes the dot's visibility to match the bubble view's state. */ |
| 153 | void updateDotVisibility(boolean animate) { |
| 154 | updateDotVisibility(animate, null /* after */); |
| 155 | } |
| 156 | |
Joshua Tsuji | 6549e70 | 2019-05-02 13:13:16 -0400 | [diff] [blame] | 157 | |
| 158 | /** |
| 159 | * Sets whether or not to hide the dot even if we'd otherwise show it. This is used while the |
| 160 | * flyout is visible or animating, to hide the dot until the flyout visually transforms into it. |
| 161 | */ |
| 162 | void setSuppressDot(boolean suppressDot, boolean animate) { |
| 163 | mSuppressDot = suppressDot; |
| 164 | updateDotVisibility(animate); |
| 165 | } |
| 166 | |
| 167 | /** Sets the position of the 'new' dot, animating it out and back in if requested. */ |
| 168 | void setDotPosition(boolean onLeft, boolean animate) { |
Lyn Han | 61d5d56 | 2019-07-01 17:39:38 -0700 | [diff] [blame] | 169 | if (animate && onLeft != mBadgedImageView.getDotOnLeft() && !mSuppressDot) { |
Joshua Tsuji | 6549e70 | 2019-05-02 13:13:16 -0400 | [diff] [blame] | 170 | animateDot(false /* showDot */, () -> { |
Lyn Han | 61d5d56 | 2019-07-01 17:39:38 -0700 | [diff] [blame] | 171 | mBadgedImageView.setDotOnLeft(onLeft); |
Joshua Tsuji | 6549e70 | 2019-05-02 13:13:16 -0400 | [diff] [blame] | 172 | animateDot(true /* showDot */, null); |
| 173 | }); |
| 174 | } else { |
Lyn Han | 61d5d56 | 2019-07-01 17:39:38 -0700 | [diff] [blame] | 175 | mBadgedImageView.setDotOnLeft(onLeft); |
Joshua Tsuji | 6549e70 | 2019-05-02 13:13:16 -0400 | [diff] [blame] | 176 | } |
| 177 | } |
| 178 | |
Lyn Han | 61d5d56 | 2019-07-01 17:39:38 -0700 | [diff] [blame] | 179 | float[] getDotCenter() { |
| 180 | float[] unscaled = mBadgedImageView.getDotCenter(); |
| 181 | return new float[]{unscaled[0], unscaled[1]}; |
| 182 | } |
| 183 | |
Joshua Tsuji | 6549e70 | 2019-05-02 13:13:16 -0400 | [diff] [blame] | 184 | boolean getDotPositionOnLeft() { |
Lyn Han | 61d5d56 | 2019-07-01 17:39:38 -0700 | [diff] [blame] | 185 | return mBadgedImageView.getDotOnLeft(); |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 186 | } |
| 187 | |
| 188 | /** |
Joshua Tsuji | dd4d9f9 | 2019-05-13 13:57:38 -0400 | [diff] [blame] | 189 | * Changes the dot's visibility to match the bubble view's state, running the provided callback |
| 190 | * after animation if requested. |
| 191 | */ |
| 192 | private void updateDotVisibility(boolean animate, Runnable after) { |
Mady Mellor | df48d0a | 2019-06-25 18:26:46 -0700 | [diff] [blame] | 193 | boolean showDot = mBubble.showBubbleDot() && !mSuppressDot; |
Joshua Tsuji | dd4d9f9 | 2019-05-13 13:57:38 -0400 | [diff] [blame] | 194 | |
| 195 | if (animate) { |
| 196 | animateDot(showDot, after); |
| 197 | } else { |
| 198 | mBadgedImageView.setShowDot(showDot); |
| 199 | } |
| 200 | } |
| 201 | |
| 202 | /** |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 203 | * Animates the badge to show or hide. |
| 204 | */ |
Joshua Tsuji | 6549e70 | 2019-05-02 13:13:16 -0400 | [diff] [blame] | 205 | private void animateDot(boolean showDot, Runnable after) { |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 206 | if (mBadgedImageView.isShowingDot() != showDot) { |
Joshua Tsuji | dd4d9f9 | 2019-05-13 13:57:38 -0400 | [diff] [blame] | 207 | if (showDot) { |
| 208 | mBadgedImageView.setShowDot(true); |
| 209 | } |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 210 | mBadgedImageView.clearAnimation(); |
| 211 | mBadgedImageView.animate().setDuration(200) |
| 212 | .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) |
| 213 | .setUpdateListener((valueAnimator) -> { |
| 214 | float fraction = valueAnimator.getAnimatedFraction(); |
Joshua Tsuji | dd4d9f9 | 2019-05-13 13:57:38 -0400 | [diff] [blame] | 215 | fraction = showDot ? fraction : 1f - fraction; |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 216 | mBadgedImageView.setDotScale(fraction); |
| 217 | }).withEndAction(() -> { |
Joshua Tsuji | 6549e70 | 2019-05-02 13:13:16 -0400 | [diff] [blame] | 218 | if (!showDot) { |
| 219 | mBadgedImageView.setShowDot(false); |
| 220 | } |
| 221 | |
| 222 | if (after != null) { |
| 223 | after.run(); |
| 224 | } |
Mark Renouf | 8eafa22 | 2019-01-23 17:01:55 -0500 | [diff] [blame] | 225 | }).start(); |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 226 | } |
| 227 | } |
| 228 | |
Lyn Han | 80b8011 | 2019-04-04 14:03:40 -0700 | [diff] [blame] | 229 | void updateViews() { |
Mady Mellor | 99a30260 | 2019-06-14 11:39:56 -0700 | [diff] [blame] | 230 | if (mBubble == null || mBubbleIconFactory == null) { |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 231 | return; |
| 232 | } |
Lyn Han | ecdd06e | 2019-07-10 18:19:37 -0700 | [diff] [blame] | 233 | // Update icon. |
Mady Mellor | 99a30260 | 2019-06-14 11:39:56 -0700 | [diff] [blame] | 234 | Notification.BubbleMetadata metadata = mBubble.getEntry().getBubbleMetadata(); |
| 235 | Notification n = mBubble.getEntry().notification.getNotification(); |
| 236 | Icon ic = metadata.getIcon(); |
| 237 | boolean needsTint = ic.getType() != Icon.TYPE_ADAPTIVE_BITMAP; |
| 238 | |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 239 | Drawable iconDrawable = ic.loadDrawable(mContext); |
Mady Mellor | 9848a6c | 2019-03-19 15:29:05 -0700 | [diff] [blame] | 240 | if (needsTint) { |
Lyn Han | ecdd06e | 2019-07-10 18:19:37 -0700 | [diff] [blame] | 241 | iconDrawable = buildIconWithTint(iconDrawable, n.color); |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 242 | } |
Lyn Han | 4c1731f | 2019-06-19 19:14:24 -0700 | [diff] [blame] | 243 | Bitmap bubbleIcon = mBubbleIconFactory.createBadgedIconBitmap(iconDrawable, |
Lyn Han | 1b4f25e | 2019-06-11 13:56:34 -0700 | [diff] [blame] | 244 | null /* user */, |
Lyn Han | 4c1731f | 2019-06-19 19:14:24 -0700 | [diff] [blame] | 245 | true /* shrinkNonAdaptiveIcons */).icon; |
Mady Mellor | d20ff41 | 2019-07-31 17:42:52 -0700 | [diff] [blame] | 246 | |
| 247 | // Give it a shadow |
| 248 | Bitmap userBadgedBitmap = mBubbleIconFactory.createIconBitmap(mUserBadgedAppIcon, |
| 249 | 1f, mBubbleIconFactory.getBadgeSize()); |
| 250 | Canvas c = new Canvas(); |
| 251 | ShadowGenerator shadowGenerator = new ShadowGenerator(mBubbleIconFactory.getBadgeSize()); |
| 252 | c.setBitmap(userBadgedBitmap); |
| 253 | shadowGenerator.recreateIcon(Bitmap.createBitmap(userBadgedBitmap), c); |
| 254 | |
| 255 | mBubbleIconFactory.badgeWithDrawable(bubbleIcon, |
| 256 | new BitmapDrawable(mContext.getResources(), userBadgedBitmap)); |
Lyn Han | 4c1731f | 2019-06-19 19:14:24 -0700 | [diff] [blame] | 257 | mBadgedImageView.setImageBitmap(bubbleIcon); |
Lyn Han | 1b4f25e | 2019-06-11 13:56:34 -0700 | [diff] [blame] | 258 | |
Lyn Han | ecdd06e | 2019-07-10 18:19:37 -0700 | [diff] [blame] | 259 | // Update badge. |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 260 | int badgeColor = determineDominateColor(iconDrawable, n.color); |
Joshua Tsuji | 6549e70 | 2019-05-02 13:13:16 -0400 | [diff] [blame] | 261 | mBadgeColor = badgeColor; |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 262 | mBadgedImageView.setDotColor(badgeColor); |
Lyn Han | ecdd06e | 2019-07-10 18:19:37 -0700 | [diff] [blame] | 263 | |
| 264 | // Update dot. |
| 265 | Path iconPath = PathParser.createPathFromPathData( |
| 266 | getResources().getString(com.android.internal.R.string.config_icon_mask)); |
| 267 | Matrix matrix = new Matrix(); |
| 268 | float scale = mBubbleIconFactory.getNormalizer().getScale(iconDrawable, |
| 269 | null /* outBounds */, null /* path */, null /* outMaskShape */); |
| 270 | float radius = BadgedImageView.DEFAULT_PATH_SIZE / 2f; |
| 271 | matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */, |
| 272 | radius /* pivot y */); |
| 273 | iconPath.transform(matrix); |
| 274 | mBadgedImageView.drawDot(iconPath); |
| 275 | |
Mady Mellor | e434827 | 2019-07-29 17:43:36 -0700 | [diff] [blame] | 276 | animateDot(mBubble.showBubbleDot() /* showDot */, null /* after */); |
Joshua Tsuji | 6549e70 | 2019-05-02 13:13:16 -0400 | [diff] [blame] | 277 | } |
| 278 | |
| 279 | int getBadgeColor() { |
| 280 | return mBadgeColor; |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 281 | } |
| 282 | |
Lyn Han | 0e82a3e | 2019-06-19 18:47:06 -0700 | [diff] [blame] | 283 | private AdaptiveIconDrawable buildIconWithTint(Drawable iconDrawable, int backgroundColor) { |
Lyn Han | 80b8011 | 2019-04-04 14:03:40 -0700 | [diff] [blame] | 284 | iconDrawable = checkTint(iconDrawable, backgroundColor); |
| 285 | InsetDrawable foreground = new InsetDrawable(iconDrawable, mIconInset); |
| 286 | ColorDrawable background = new ColorDrawable(backgroundColor); |
| 287 | return new AdaptiveIconDrawable(background, foreground); |
| 288 | } |
| 289 | |
| 290 | private Drawable checkTint(Drawable iconDrawable, int backgroundColor) { |
Lyn Han | 56a3ec5 | 2019-03-25 15:04:21 -0700 | [diff] [blame] | 291 | backgroundColor = ColorUtils.setAlphaComponent(backgroundColor, 255 /* alpha */); |
| 292 | if (backgroundColor == Color.TRANSPARENT) { |
| 293 | // ColorUtils throws exception when background is translucent. |
| 294 | backgroundColor = DEFAULT_BACKGROUND_COLOR; |
| 295 | } |
| 296 | iconDrawable.setTint(Color.WHITE); |
| 297 | double contrastRatio = ColorUtils.calculateContrast(Color.WHITE, backgroundColor); |
| 298 | if (contrastRatio < ICON_MIN_CONTRAST) { |
| 299 | int dark = ColorUtils.setAlphaComponent(Color.BLACK, DARK_ICON_ALPHA); |
| 300 | iconDrawable.setTint(dark); |
| 301 | } |
Lyn Han | 80b8011 | 2019-04-04 14:03:40 -0700 | [diff] [blame] | 302 | return iconDrawable; |
Lyn Han | 56a3ec5 | 2019-03-25 15:04:21 -0700 | [diff] [blame] | 303 | } |
| 304 | |
Mady Mellor | 3f2efdb | 2018-11-21 11:30:45 -0800 | [diff] [blame] | 305 | private int determineDominateColor(Drawable d, int defaultTint) { |
| 306 | // XXX: should we pull from the drawable, app icon, notif tint? |
| 307 | return ColorUtils.blendARGB(defaultTint, Color.WHITE, WHITE_SCRIM_ALPHA); |
Mady Mellor | c3d6f7d | 2018-11-07 09:36:56 -0800 | [diff] [blame] | 308 | } |
Mady Mellor | c3d6f7d | 2018-11-07 09:36:56 -0800 | [diff] [blame] | 309 | } |