1 /* 2 * Copyright (C) 2017 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.keyguard; 18 19 import static android.app.slice.Slice.HINT_LIST_ITEM; 20 import static android.view.Display.DEFAULT_DISPLAY; 21 import static android.view.Display.INVALID_DISPLAY; 22 23 import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT; 24 25 import android.animation.LayoutTransition; 26 import android.animation.ObjectAnimator; 27 import android.animation.PropertyValuesHolder; 28 import android.annotation.ColorInt; 29 import android.annotation.StyleRes; 30 import android.app.PendingIntent; 31 import android.content.Context; 32 import android.content.res.Resources; 33 import android.graphics.Color; 34 import android.graphics.drawable.Drawable; 35 import android.graphics.text.LineBreaker; 36 import android.net.Uri; 37 import android.os.Trace; 38 import android.provider.Settings; 39 import android.text.TextUtils; 40 import android.text.TextUtils.TruncateAt; 41 import android.util.AttributeSet; 42 import android.util.Log; 43 import android.util.TypedValue; 44 import android.view.Display; 45 import android.view.View; 46 import android.view.animation.Animation; 47 import android.widget.LinearLayout; 48 import android.widget.TextView; 49 50 import androidx.lifecycle.LiveData; 51 import androidx.lifecycle.Observer; 52 import androidx.slice.Slice; 53 import androidx.slice.SliceItem; 54 import androidx.slice.SliceViewManager; 55 import androidx.slice.core.SliceQuery; 56 import androidx.slice.widget.ListContent; 57 import androidx.slice.widget.RowContent; 58 import androidx.slice.widget.SliceContent; 59 import androidx.slice.widget.SliceLiveData; 60 61 import com.android.internal.annotations.VisibleForTesting; 62 import com.android.internal.graphics.ColorUtils; 63 import com.android.settingslib.Utils; 64 import com.android.systemui.Dependency; 65 import com.android.systemui.Interpolators; 66 import com.android.systemui.R; 67 import com.android.systemui.dagger.qualifiers.Main; 68 import com.android.systemui.keyguard.KeyguardSliceProvider; 69 import com.android.systemui.plugins.ActivityStarter; 70 import com.android.systemui.statusbar.policy.ConfigurationController; 71 import com.android.systemui.tuner.TunerService; 72 import com.android.systemui.util.wakelock.KeepAwakeAnimationListener; 73 74 import java.io.FileDescriptor; 75 import java.io.PrintWriter; 76 import java.util.ArrayList; 77 import java.util.HashMap; 78 import java.util.List; 79 80 import javax.inject.Inject; 81 import javax.inject.Named; 82 83 /** 84 * View visible under the clock on the lock screen and AoD. 85 */ 86 public class KeyguardSliceView extends LinearLayout implements View.OnClickListener, 87 Observer<Slice>, TunerService.Tunable, ConfigurationController.ConfigurationListener { 88 89 private static final String TAG = "KeyguardSliceView"; 90 public static final int DEFAULT_ANIM_DURATION = 550; 91 92 private final HashMap<View, PendingIntent> mClickActions; 93 private final ActivityStarter mActivityStarter; 94 private final ConfigurationController mConfigurationController; 95 private final LayoutTransition mLayoutTransition; 96 private final TunerService mTunerService; 97 private Uri mKeyguardSliceUri; 98 @VisibleForTesting 99 TextView mTitle; 100 private Row mRow; 101 private int mTextColor; 102 private float mDarkAmount = 0; 103 104 private LiveData<Slice> mLiveData; 105 private int mDisplayId = INVALID_DISPLAY; 106 private int mIconSize; 107 private int mIconSizeWithHeader; 108 /** 109 * Runnable called whenever the view contents change. 110 */ 111 private Runnable mContentChangeListener; 112 private Slice mSlice; 113 private boolean mHasHeader; 114 private final int mRowWithHeaderPadding; 115 private final int mRowPadding; 116 private float mRowTextSize; 117 private float mRowWithHeaderTextSize; 118 119 @Inject KeyguardSliceView(@amedVIEW_CONTEXT) Context context, AttributeSet attrs, ActivityStarter activityStarter, ConfigurationController configurationController, TunerService tunerService, @Main Resources resources)120 public KeyguardSliceView(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs, 121 ActivityStarter activityStarter, ConfigurationController configurationController, 122 TunerService tunerService, @Main Resources resources) { 123 super(context, attrs); 124 125 mTunerService = tunerService; 126 mClickActions = new HashMap<>(); 127 mRowPadding = resources.getDimensionPixelSize(R.dimen.subtitle_clock_padding); 128 mRowWithHeaderPadding = resources.getDimensionPixelSize(R.dimen.header_subtitle_padding); 129 mActivityStarter = activityStarter; 130 mConfigurationController = configurationController; 131 132 mLayoutTransition = new LayoutTransition(); 133 mLayoutTransition.setStagger(LayoutTransition.CHANGE_APPEARING, DEFAULT_ANIM_DURATION / 2); 134 mLayoutTransition.setDuration(LayoutTransition.APPEARING, DEFAULT_ANIM_DURATION); 135 mLayoutTransition.setDuration(LayoutTransition.DISAPPEARING, DEFAULT_ANIM_DURATION / 2); 136 mLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_APPEARING); 137 mLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING); 138 mLayoutTransition.setInterpolator(LayoutTransition.APPEARING, 139 Interpolators.FAST_OUT_SLOW_IN); 140 mLayoutTransition.setInterpolator(LayoutTransition.DISAPPEARING, Interpolators.ALPHA_OUT); 141 mLayoutTransition.setAnimateParentHierarchy(false); 142 } 143 144 @Override onFinishInflate()145 protected void onFinishInflate() { 146 super.onFinishInflate(); 147 mTitle = findViewById(R.id.title); 148 mRow = findViewById(R.id.row); 149 mTextColor = Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor); 150 mIconSize = (int) mContext.getResources().getDimension(R.dimen.widget_icon_size); 151 mIconSizeWithHeader = (int) mContext.getResources().getDimension(R.dimen.header_icon_size); 152 mRowTextSize = mContext.getResources().getDimensionPixelSize( 153 R.dimen.widget_label_font_size); 154 mRowWithHeaderTextSize = mContext.getResources().getDimensionPixelSize( 155 R.dimen.header_row_font_size); 156 mTitle.setOnClickListener(this); 157 mTitle.setBreakStrategy(LineBreaker.BREAK_STRATEGY_BALANCED); 158 } 159 160 @Override onAttachedToWindow()161 protected void onAttachedToWindow() { 162 super.onAttachedToWindow(); 163 164 Display display = getDisplay(); 165 if (display != null) { 166 mDisplayId = display.getDisplayId(); 167 } 168 mTunerService.addTunable(this, Settings.Secure.KEYGUARD_SLICE_URI); 169 // Make sure we always have the most current slice 170 if (mDisplayId == DEFAULT_DISPLAY) { 171 mLiveData.observeForever(this); 172 } 173 mConfigurationController.addCallback(this); 174 } 175 176 @Override onDetachedFromWindow()177 protected void onDetachedFromWindow() { 178 super.onDetachedFromWindow(); 179 180 // TODO(b/117344873) Remove below work around after this issue be fixed. 181 if (mDisplayId == DEFAULT_DISPLAY) { 182 mLiveData.removeObserver(this); 183 } 184 mTunerService.removeTunable(this); 185 mConfigurationController.removeCallback(this); 186 } 187 188 @Override onVisibilityAggregated(boolean isVisible)189 public void onVisibilityAggregated(boolean isVisible) { 190 super.onVisibilityAggregated(isVisible); 191 setLayoutTransition(isVisible ? mLayoutTransition : null); 192 } 193 194 /** 195 * Returns whether the current visible slice has a title/header. 196 */ hasHeader()197 public boolean hasHeader() { 198 return mHasHeader; 199 } 200 showSlice()201 private void showSlice() { 202 Trace.beginSection("KeyguardSliceView#showSlice"); 203 if (mSlice == null) { 204 mTitle.setVisibility(GONE); 205 mRow.setVisibility(GONE); 206 mHasHeader = false; 207 if (mContentChangeListener != null) { 208 mContentChangeListener.run(); 209 } 210 Trace.endSection(); 211 return; 212 } 213 mClickActions.clear(); 214 215 ListContent lc = new ListContent(getContext(), mSlice); 216 SliceContent headerContent = lc.getHeader(); 217 mHasHeader = headerContent != null && !headerContent.getSliceItem().hasHint(HINT_LIST_ITEM); 218 List<SliceContent> subItems = new ArrayList<>(); 219 for (int i = 0; i < lc.getRowItems().size(); i++) { 220 SliceContent subItem = lc.getRowItems().get(i); 221 String itemUri = subItem.getSliceItem().getSlice().getUri().toString(); 222 // Filter out the action row 223 if (!KeyguardSliceProvider.KEYGUARD_ACTION_URI.equals(itemUri)) { 224 subItems.add(subItem); 225 } 226 } 227 if (!mHasHeader) { 228 mTitle.setVisibility(GONE); 229 } else { 230 mTitle.setVisibility(VISIBLE); 231 232 RowContent header = lc.getHeader(); 233 SliceItem mainTitle = header.getTitleItem(); 234 CharSequence title = mainTitle != null ? mainTitle.getText() : null; 235 mTitle.setText(title); 236 if (header.getPrimaryAction() != null 237 && header.getPrimaryAction().getAction() != null) { 238 mClickActions.put(mTitle, header.getPrimaryAction().getAction()); 239 } 240 } 241 242 final int subItemsCount = subItems.size(); 243 final int blendedColor = getTextColor(); 244 final int startIndex = mHasHeader ? 1 : 0; // First item is header; skip it 245 mRow.setVisibility(subItemsCount > 0 ? VISIBLE : GONE); 246 LinearLayout.LayoutParams layoutParams = (LayoutParams) mRow.getLayoutParams(); 247 layoutParams.topMargin = mHasHeader ? mRowWithHeaderPadding : mRowPadding; 248 mRow.setLayoutParams(layoutParams); 249 250 for (int i = startIndex; i < subItemsCount; i++) { 251 RowContent rc = (RowContent) subItems.get(i); 252 SliceItem item = rc.getSliceItem(); 253 final Uri itemTag = item.getSlice().getUri(); 254 // Try to reuse the view if already exists in the layout 255 KeyguardSliceTextView button = mRow.findViewWithTag(itemTag); 256 if (button == null) { 257 button = new KeyguardSliceTextView(mContext); 258 button.setTextColor(blendedColor); 259 button.setTag(itemTag); 260 final int viewIndex = i - (mHasHeader ? 1 : 0); 261 mRow.addView(button, viewIndex); 262 } 263 264 PendingIntent pendingIntent = null; 265 if (rc.getPrimaryAction() != null) { 266 pendingIntent = rc.getPrimaryAction().getAction(); 267 } 268 mClickActions.put(button, pendingIntent); 269 270 final SliceItem titleItem = rc.getTitleItem(); 271 button.setText(titleItem == null ? null : titleItem.getText()); 272 button.setContentDescription(rc.getContentDescription()); 273 button.setTextSize(TypedValue.COMPLEX_UNIT_PX, 274 mHasHeader ? mRowWithHeaderTextSize : mRowTextSize); 275 276 Drawable iconDrawable = null; 277 SliceItem icon = SliceQuery.find(item.getSlice(), 278 android.app.slice.SliceItem.FORMAT_IMAGE); 279 if (icon != null) { 280 final int iconSize = mHasHeader ? mIconSizeWithHeader : mIconSize; 281 iconDrawable = icon.getIcon().loadDrawable(mContext); 282 if (iconDrawable != null) { 283 final int width = (int) (iconDrawable.getIntrinsicWidth() 284 / (float) iconDrawable.getIntrinsicHeight() * iconSize); 285 iconDrawable.setBounds(0, 0, Math.max(width, 1), iconSize); 286 } 287 } 288 button.setCompoundDrawables(iconDrawable, null, null, null); 289 button.setOnClickListener(this); 290 button.setClickable(pendingIntent != null); 291 } 292 293 // Removing old views 294 for (int i = 0; i < mRow.getChildCount(); i++) { 295 View child = mRow.getChildAt(i); 296 if (!mClickActions.containsKey(child)) { 297 mRow.removeView(child); 298 i--; 299 } 300 } 301 302 if (mContentChangeListener != null) { 303 mContentChangeListener.run(); 304 } 305 Trace.endSection(); 306 } 307 setDarkAmount(float darkAmount)308 public void setDarkAmount(float darkAmount) { 309 mDarkAmount = darkAmount; 310 mRow.setDarkAmount(darkAmount); 311 updateTextColors(); 312 } 313 updateTextColors()314 private void updateTextColors() { 315 final int blendedColor = getTextColor(); 316 mTitle.setTextColor(blendedColor); 317 int childCount = mRow.getChildCount(); 318 for (int i = 0; i < childCount; i++) { 319 View v = mRow.getChildAt(i); 320 if (v instanceof TextView) { 321 ((TextView) v).setTextColor(blendedColor); 322 } 323 } 324 } 325 326 @Override onClick(View v)327 public void onClick(View v) { 328 final PendingIntent action = mClickActions.get(v); 329 if (action != null) { 330 mActivityStarter.startPendingIntentDismissingKeyguard(action); 331 } 332 } 333 334 /** 335 * Runnable that gets invoked every time the title or the row visibility changes. 336 * @param contentChangeListener The listener. 337 */ setContentChangeListener(Runnable contentChangeListener)338 public void setContentChangeListener(Runnable contentChangeListener) { 339 mContentChangeListener = contentChangeListener; 340 } 341 342 /** 343 * LiveData observer lifecycle. 344 * @param slice the new slice content. 345 */ 346 @Override onChanged(Slice slice)347 public void onChanged(Slice slice) { 348 mSlice = slice; 349 showSlice(); 350 } 351 352 @Override onTuningChanged(String key, String newValue)353 public void onTuningChanged(String key, String newValue) { 354 setupUri(newValue); 355 } 356 357 /** 358 * Sets the slice provider Uri. 359 */ setupUri(String uriString)360 public void setupUri(String uriString) { 361 if (uriString == null) { 362 uriString = KeyguardSliceProvider.KEYGUARD_SLICE_URI; 363 } 364 365 boolean wasObserving = false; 366 if (mLiveData != null && mLiveData.hasActiveObservers()) { 367 wasObserving = true; 368 mLiveData.removeObserver(this); 369 } 370 371 mKeyguardSliceUri = Uri.parse(uriString); 372 mLiveData = SliceLiveData.fromUri(mContext, mKeyguardSliceUri); 373 374 if (wasObserving) { 375 mLiveData.observeForever(this); 376 } 377 } 378 379 @VisibleForTesting getTextColor()380 int getTextColor() { 381 return ColorUtils.blendARGB(mTextColor, Color.WHITE, mDarkAmount); 382 } 383 384 @VisibleForTesting setTextColor(@olorInt int textColor)385 void setTextColor(@ColorInt int textColor) { 386 mTextColor = textColor; 387 updateTextColors(); 388 } 389 390 @Override onDensityOrFontScaleChanged()391 public void onDensityOrFontScaleChanged() { 392 mIconSize = mContext.getResources().getDimensionPixelSize(R.dimen.widget_icon_size); 393 mIconSizeWithHeader = (int) mContext.getResources().getDimension(R.dimen.header_icon_size); 394 mRowTextSize = mContext.getResources().getDimensionPixelSize( 395 R.dimen.widget_label_font_size); 396 mRowWithHeaderTextSize = mContext.getResources().getDimensionPixelSize( 397 R.dimen.header_row_font_size); 398 } 399 refresh()400 public void refresh() { 401 Slice slice; 402 Trace.beginSection("KeyguardSliceView#refresh"); 403 // We can optimize performance and avoid binder calls when we know that we're bound 404 // to a Slice on the same process. 405 if (KeyguardSliceProvider.KEYGUARD_SLICE_URI.equals(mKeyguardSliceUri.toString())) { 406 KeyguardSliceProvider instance = KeyguardSliceProvider.getAttachedInstance(); 407 if (instance != null) { 408 slice = instance.onBindSlice(mKeyguardSliceUri); 409 } else { 410 Log.w(TAG, "Keyguard slice not bound yet?"); 411 slice = null; 412 } 413 } else { 414 slice = SliceViewManager.getInstance(getContext()).bindSlice(mKeyguardSliceUri); 415 } 416 onChanged(slice); 417 Trace.endSection(); 418 } 419 dump(FileDescriptor fd, PrintWriter pw, String[] args)420 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 421 pw.println("KeyguardSliceView:"); 422 pw.println(" mClickActions: " + mClickActions); 423 pw.println(" mTitle: " + (mTitle == null ? "null" : mTitle.getVisibility() == VISIBLE)); 424 pw.println(" mRow: " + (mRow == null ? "null" : mRow.getVisibility() == VISIBLE)); 425 pw.println(" mTextColor: " + Integer.toHexString(mTextColor)); 426 pw.println(" mDarkAmount: " + mDarkAmount); 427 pw.println(" mSlice: " + mSlice); 428 pw.println(" mHasHeader: " + mHasHeader); 429 } 430 431 public static class Row extends LinearLayout { 432 433 /** 434 * This view is visible in AOD, which means that the device will sleep if we 435 * don't hold a wake lock. We want to enter doze only after all views have reached 436 * their desired positions. 437 */ 438 private final Animation.AnimationListener mKeepAwakeListener; 439 private LayoutTransition mLayoutTransition; 440 private float mDarkAmount; 441 Row(Context context)442 public Row(Context context) { 443 this(context, null); 444 } 445 Row(Context context, AttributeSet attrs)446 public Row(Context context, AttributeSet attrs) { 447 this(context, attrs, 0); 448 } 449 Row(Context context, AttributeSet attrs, int defStyleAttr)450 public Row(Context context, AttributeSet attrs, int defStyleAttr) { 451 this(context, attrs, defStyleAttr, 0); 452 } 453 Row(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)454 public Row(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 455 super(context, attrs, defStyleAttr, defStyleRes); 456 mKeepAwakeListener = new KeepAwakeAnimationListener(mContext); 457 } 458 459 @Override onFinishInflate()460 protected void onFinishInflate() { 461 mLayoutTransition = new LayoutTransition(); 462 mLayoutTransition.setDuration(DEFAULT_ANIM_DURATION); 463 464 PropertyValuesHolder left = PropertyValuesHolder.ofInt("left", 0, 1); 465 PropertyValuesHolder right = PropertyValuesHolder.ofInt("right", 0, 1); 466 ObjectAnimator changeAnimator = ObjectAnimator.ofPropertyValuesHolder((Object) null, 467 left, right); 468 mLayoutTransition.setAnimator(LayoutTransition.CHANGE_APPEARING, changeAnimator); 469 mLayoutTransition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, changeAnimator); 470 mLayoutTransition.setInterpolator(LayoutTransition.CHANGE_APPEARING, 471 Interpolators.ACCELERATE_DECELERATE); 472 mLayoutTransition.setInterpolator(LayoutTransition.CHANGE_DISAPPEARING, 473 Interpolators.ACCELERATE_DECELERATE); 474 mLayoutTransition.setStartDelay(LayoutTransition.CHANGE_APPEARING, 475 DEFAULT_ANIM_DURATION); 476 mLayoutTransition.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 477 DEFAULT_ANIM_DURATION); 478 479 ObjectAnimator appearAnimator = ObjectAnimator.ofFloat(null, "alpha", 0f, 1f); 480 mLayoutTransition.setAnimator(LayoutTransition.APPEARING, appearAnimator); 481 mLayoutTransition.setInterpolator(LayoutTransition.APPEARING, Interpolators.ALPHA_IN); 482 483 ObjectAnimator disappearAnimator = ObjectAnimator.ofFloat(null, "alpha", 1f, 0f); 484 mLayoutTransition.setInterpolator(LayoutTransition.DISAPPEARING, 485 Interpolators.ALPHA_OUT); 486 mLayoutTransition.setDuration(LayoutTransition.DISAPPEARING, DEFAULT_ANIM_DURATION / 4); 487 mLayoutTransition.setAnimator(LayoutTransition.DISAPPEARING, disappearAnimator); 488 489 mLayoutTransition.setAnimateParentHierarchy(false); 490 } 491 492 @Override onVisibilityAggregated(boolean isVisible)493 public void onVisibilityAggregated(boolean isVisible) { 494 super.onVisibilityAggregated(isVisible); 495 setLayoutTransition(isVisible ? mLayoutTransition : null); 496 } 497 498 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)499 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 500 int width = MeasureSpec.getSize(widthMeasureSpec); 501 int childCount = getChildCount(); 502 for (int i = 0; i < childCount; i++) { 503 View child = getChildAt(i); 504 if (child instanceof KeyguardSliceTextView) { 505 ((KeyguardSliceTextView) child).setMaxWidth(width / 3); 506 } 507 } 508 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 509 } 510 setDarkAmount(float darkAmount)511 public void setDarkAmount(float darkAmount) { 512 boolean isAwake = darkAmount != 0; 513 boolean wasAwake = mDarkAmount != 0; 514 if (isAwake == wasAwake) { 515 return; 516 } 517 mDarkAmount = darkAmount; 518 setLayoutAnimationListener(isAwake ? null : mKeepAwakeListener); 519 } 520 521 @Override hasOverlappingRendering()522 public boolean hasOverlappingRendering() { 523 return false; 524 } 525 } 526 527 /** 528 * Representation of an item that appears under the clock on main keyguard message. 529 */ 530 @VisibleForTesting 531 static class KeyguardSliceTextView extends TextView implements 532 ConfigurationController.ConfigurationListener { 533 534 @StyleRes 535 private static int sStyleId = R.style.TextAppearance_Keyguard_Secondary; 536 KeyguardSliceTextView(Context context)537 KeyguardSliceTextView(Context context) { 538 super(context, null /* attrs */, 0 /* styleAttr */, sStyleId); 539 onDensityOrFontScaleChanged(); 540 setEllipsize(TruncateAt.END); 541 } 542 543 @Override onAttachedToWindow()544 protected void onAttachedToWindow() { 545 super.onAttachedToWindow(); 546 Dependency.get(ConfigurationController.class).addCallback(this); 547 } 548 549 @Override onDetachedFromWindow()550 protected void onDetachedFromWindow() { 551 super.onDetachedFromWindow(); 552 Dependency.get(ConfigurationController.class).removeCallback(this); 553 } 554 555 @Override onDensityOrFontScaleChanged()556 public void onDensityOrFontScaleChanged() { 557 updatePadding(); 558 } 559 560 @Override onOverlayChanged()561 public void onOverlayChanged() { 562 setTextAppearance(sStyleId); 563 } 564 565 @Override setText(CharSequence text, BufferType type)566 public void setText(CharSequence text, BufferType type) { 567 super.setText(text, type); 568 updatePadding(); 569 } 570 updatePadding()571 private void updatePadding() { 572 boolean hasText = !TextUtils.isEmpty(getText()); 573 int horizontalPadding = (int) getContext().getResources() 574 .getDimension(R.dimen.widget_horizontal_padding) / 2; 575 setPadding(horizontalPadding, 0, horizontalPadding * (hasText ? 1 : -1), 0); 576 setCompoundDrawablePadding((int) mContext.getResources() 577 .getDimension(R.dimen.widget_icon_padding)); 578 } 579 580 @Override setTextColor(int color)581 public void setTextColor(int color) { 582 super.setTextColor(color); 583 updateDrawableColors(); 584 } 585 586 @Override setCompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottom)587 public void setCompoundDrawables(Drawable left, Drawable top, Drawable right, 588 Drawable bottom) { 589 super.setCompoundDrawables(left, top, right, bottom); 590 updateDrawableColors(); 591 updatePadding(); 592 } 593 updateDrawableColors()594 private void updateDrawableColors() { 595 final int color = getCurrentTextColor(); 596 for (Drawable drawable : getCompoundDrawables()) { 597 if (drawable != null) { 598 drawable.setTint(color); 599 } 600 } 601 } 602 } 603 } 604