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