• 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.launcher3.views;
18 
19 import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
20 
21 import android.content.Context;
22 import android.graphics.Canvas;
23 import android.graphics.Color;
24 import android.graphics.drawable.Drawable;
25 import android.os.Build;
26 import android.text.Spannable;
27 import android.text.SpannableString;
28 import android.text.TextUtils;
29 import android.text.style.ImageSpan;
30 import android.util.AttributeSet;
31 import android.util.Log;
32 
33 import androidx.annotation.DrawableRes;
34 import androidx.annotation.NonNull;
35 import androidx.annotation.RequiresApi;
36 
37 import com.android.launcher3.BubbleTextView;
38 import com.android.launcher3.R;
39 
40 /**
41  * Extension of {@link BubbleTextView} which draws two shadows on the text (ambient and key shadows}
42  */
43 public class DoubleShadowBubbleTextView extends BubbleTextView {
44 
45     private final ShadowInfo mShadowInfo;
46 
DoubleShadowBubbleTextView(Context context)47     public DoubleShadowBubbleTextView(Context context) {
48         this(context, null);
49     }
50 
DoubleShadowBubbleTextView(Context context, AttributeSet attrs)51     public DoubleShadowBubbleTextView(Context context, AttributeSet attrs) {
52         this(context, attrs, 0);
53     }
54 
DoubleShadowBubbleTextView(Context context, AttributeSet attrs, int defStyle)55     public DoubleShadowBubbleTextView(Context context, AttributeSet attrs, int defStyle) {
56         super(context, attrs, defStyle);
57         mShadowInfo = ShadowInfo.Companion.fromContext(context, attrs, defStyle);
58         setShadowLayer(
59                 mShadowInfo.getAmbientShadowBlur(),
60                 0,
61                 0,
62                 mShadowInfo.getAmbientShadowColor()
63         );
64     }
65 
66     @Override
setTextWithStartIcon(CharSequence text, @DrawableRes int drawableId)67     public void setTextWithStartIcon(CharSequence text, @DrawableRes int drawableId) {
68         Drawable drawable = getContext().getDrawable(drawableId);
69         if (drawable == null) {
70             setText(text);
71             Log.w(TAG, "setTextWithStartIcon: start icon Drawable not found from resources"
72                     + ", will just set text instead.");
73             return;
74         }
75         drawable.setTint(getCurrentTextColor());
76         int textSize = Math.round(getTextSize());
77         ImageSpan imageSpan;
78         if (!skipDoubleShadow() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
79             drawable = getDoubleShadowDrawable(drawable, textSize);
80         }
81         drawable.setBounds(0, 0, textSize, textSize);
82         imageSpan = new ImageSpan(drawable, ImageSpan.ALIGN_CENTER);
83         // First space will be replaced with Drawable, second space is for space before text.
84         SpannableString spannable = new SpannableString("  " + text);
85         spannable.setSpan(imageSpan, 0, 1, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
86         setText(spannable);
87     }
88 
89     @RequiresApi(Build.VERSION_CODES.S)
getDoubleShadowDrawable( @onNull Drawable drawable, int textSize )90     private DoubleShadowIconDrawable getDoubleShadowDrawable(
91             @NonNull Drawable drawable, int textSize
92     ) {
93         // add some padding via inset to avoid shadow clipping
94         int iconInsetSize = getContext().getResources()
95                 .getDimensionPixelSize(R.dimen.app_title_icon_shadow_inset);
96         return new DoubleShadowIconDrawable(
97                 mShadowInfo,
98                 drawable,
99                 textSize,
100                 iconInsetSize
101         );
102     }
103 
104     @Override
onDraw(Canvas canvas)105     public void onDraw(Canvas canvas) {
106         if (shouldDrawAppContrastTile() && !TextUtils.isEmpty(getText())) {
107             drawAppContrastTile(canvas);
108         }
109         // If text is transparent or shadow alpha is 0, don't draw any shadow
110         if (skipDoubleShadow()) {
111             super.onDraw(canvas);
112             return;
113         }
114         int alpha = Color.alpha(getCurrentTextColor());
115 
116         // We enhance the shadow by drawing the shadow twice
117         getPaint().setShadowLayer(mShadowInfo.getAmbientShadowBlur(), 0, 0,
118                 getTextShadowColor(mShadowInfo.getAmbientShadowColor(), alpha));
119 
120         drawWithoutDot(canvas);
121         canvas.save();
122         canvas.clipRect(getScrollX(), getScrollY() + getExtendedPaddingTop(),
123                 getScrollX() + getWidth(),
124                 getScrollY() + getHeight());
125 
126         getPaint().setShadowLayer(
127                 mShadowInfo.getKeyShadowBlur(),
128                 mShadowInfo.getKeyShadowOffsetX(),
129                 mShadowInfo.getKeyShadowOffsetY(),
130                 getTextShadowColor(mShadowInfo.getKeyShadowColor(), alpha));
131         drawWithoutDot(canvas);
132         canvas.restore();
133 
134         drawDotIfNecessary(canvas);
135         drawRunningAppIndicatorIfNecessary(canvas);
136     }
137 
skipDoubleShadow()138     private boolean skipDoubleShadow() {
139         int textAlpha = Color.alpha(getCurrentTextColor());
140         int keyShadowAlpha = Color.alpha(mShadowInfo.getKeyShadowColor());
141         int ambientShadowAlpha = Color.alpha(mShadowInfo.getAmbientShadowColor());
142         if (textAlpha == 0 || (keyShadowAlpha == 0 && ambientShadowAlpha == 0)) {
143             getPaint().clearShadowLayer();
144             return true;
145         } else if (ambientShadowAlpha > 0 && keyShadowAlpha == 0) {
146             getPaint().setShadowLayer(mShadowInfo.getAmbientShadowBlur(), 0, 0,
147                     getTextShadowColor(mShadowInfo.getAmbientShadowColor(), textAlpha));
148             return true;
149         } else if (keyShadowAlpha > 0 && ambientShadowAlpha == 0) {
150             getPaint().setShadowLayer(
151                     mShadowInfo.getKeyShadowBlur(),
152                     mShadowInfo.getKeyShadowOffsetX(),
153                     mShadowInfo.getKeyShadowOffsetY(),
154                     getTextShadowColor(mShadowInfo.getKeyShadowColor(), textAlpha));
155             return true;
156         } else {
157             return false;
158         }
159     }
160 
161 
162     // Multiplies the alpha of shadowColor by textAlpha.
getTextShadowColor(int shadowColor, int textAlpha)163     private static int getTextShadowColor(int shadowColor, int textAlpha) {
164         return setColorAlphaBound(shadowColor,
165                 Math.round(Color.alpha(shadowColor) * textAlpha / 255f));
166     }
167 }
168