• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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