• 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"); you may not use this file
5  * except in compliance with the License. You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the
10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11  * KIND, either express or implied. See the License for the specific language governing
12  * permissions and limitations under the License.
13  */
14 
15 package com.android.systemui.qs;
16 
17 import static com.android.systemui.qs.tileimpl.QSIconViewImpl.QS_ANIM_LENGTH;
18 
19 import android.animation.ObjectAnimator;
20 import android.animation.ValueAnimator;
21 import android.annotation.ColorInt;
22 import android.annotation.IntRange;
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.content.res.ColorStateList;
26 import android.graphics.Canvas;
27 import android.graphics.ColorFilter;
28 import android.graphics.Matrix;
29 import android.graphics.Paint;
30 import android.graphics.Path;
31 import android.graphics.Path.Direction;
32 import android.graphics.PorterDuff.Mode;
33 import android.graphics.Rect;
34 import android.graphics.RectF;
35 import android.graphics.drawable.Drawable;
36 import android.util.FloatProperty;
37 
38 public class SlashDrawable extends Drawable {
39 
40     public static final float CORNER_RADIUS = 1f;
41 
42     private final Path mPath = new Path();
43     private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
44 
45     // These values are derived in un-rotated (vertical) orientation
46     private static final float SLASH_WIDTH = 1.8384776f;
47     private static final float SLASH_HEIGHT = 28f;
48     private static final float CENTER_X = 10.65f;
49     private static final float CENTER_Y = 11.869239f;
50     private static final float SCALE = 24f;
51 
52     // Bottom is derived during animation
53     private static final float LEFT = (CENTER_X - (SLASH_WIDTH / 2)) / SCALE;
54     private static final float TOP = (CENTER_Y - (SLASH_HEIGHT / 2)) / SCALE;
55     private static final float RIGHT = (CENTER_X + (SLASH_WIDTH / 2)) / SCALE;
56     // Draw the slash washington-monument style; rotate to no-u-turn style
57     private static final float DEFAULT_ROTATION = -45f;
58 
59     private Drawable mDrawable;
60     private final RectF mSlashRect = new RectF(0, 0, 0, 0);
61     private float mRotation;
62     private boolean mSlashed;
63     private Mode mTintMode;
64     private ColorStateList mTintList;
65     private boolean mAnimationEnabled = true;
66 
SlashDrawable(Drawable d)67     public SlashDrawable(Drawable d) {
68         mDrawable = d;
69     }
70 
71     @Override
getIntrinsicHeight()72     public int getIntrinsicHeight() {
73         return mDrawable != null ? mDrawable.getIntrinsicHeight(): 0;
74     }
75 
76     @Override
getIntrinsicWidth()77     public int getIntrinsicWidth() {
78         return mDrawable != null ? mDrawable.getIntrinsicWidth(): 0;
79     }
80 
81     @Override
onBoundsChange(Rect bounds)82     protected void onBoundsChange(Rect bounds) {
83         super.onBoundsChange(bounds);
84         mDrawable.setBounds(bounds);
85     }
86 
setDrawable(Drawable d)87     public void setDrawable(Drawable d) {
88         mDrawable = d;
89         mDrawable.setCallback(getCallback());
90         mDrawable.setBounds(getBounds());
91         if (mTintMode != null) mDrawable.setTintMode(mTintMode);
92         if (mTintList != null) mDrawable.setTintList(mTintList);
93         invalidateSelf();
94     }
95 
setRotation(float rotation)96     public void setRotation(float rotation) {
97         if (mRotation == rotation) return;
98         mRotation = rotation;
99         invalidateSelf();
100     }
101 
setAnimationEnabled(boolean enabled)102     public void setAnimationEnabled(boolean enabled) {
103         mAnimationEnabled = enabled;
104     }
105 
106     // Animate this value on change
107     private float mCurrentSlashLength;
108     private final FloatProperty mSlashLengthProp = new FloatProperty<SlashDrawable>("slashLength") {
109         @Override
110         public void setValue(SlashDrawable object, float value) {
111             object.mCurrentSlashLength = value;
112         }
113 
114         @Override
115         public Float get(SlashDrawable object) {
116             return object.mCurrentSlashLength;
117         }
118     };
119 
setSlashed(boolean slashed)120     public void setSlashed(boolean slashed) {
121         if (mSlashed == slashed) return;
122 
123         mSlashed = slashed;
124 
125         final float end = mSlashed ? SLASH_HEIGHT / SCALE : 0f;
126         final float start = mSlashed ? 0f : SLASH_HEIGHT / SCALE;
127 
128         if (mAnimationEnabled) {
129             ObjectAnimator anim = ObjectAnimator.ofFloat(this, mSlashLengthProp, start, end);
130             anim.addUpdateListener((ValueAnimator valueAnimator) -> invalidateSelf());
131             anim.setDuration(QS_ANIM_LENGTH);
132             anim.start();
133         } else {
134             mCurrentSlashLength = end;
135             invalidateSelf();
136         }
137     }
138 
139     @Override
draw(@onNull Canvas canvas)140     public void draw(@NonNull Canvas canvas) {
141         canvas.save();
142         Matrix m = new Matrix();
143         final int width = getBounds().width();
144         final int height = getBounds().height();
145         final float radiusX = scale(CORNER_RADIUS, width);
146         final float radiusY = scale(CORNER_RADIUS, height);
147         updateRect(
148                 scale(LEFT, width),
149                 scale(TOP, height),
150                 scale(RIGHT, width),
151                 scale(TOP + mCurrentSlashLength, height)
152         );
153 
154         mPath.reset();
155         // Draw the slash vertically
156         mPath.addRoundRect(mSlashRect, radiusX, radiusY, Direction.CW);
157         // Rotate -45 + desired rotation
158         m.setRotate(mRotation + DEFAULT_ROTATION, width / 2, height / 2);
159         mPath.transform(m);
160         canvas.drawPath(mPath, mPaint);
161 
162         // Rotate back to vertical
163         m.setRotate(-mRotation - DEFAULT_ROTATION, width / 2, height / 2);
164         mPath.transform(m);
165 
166         // Draw another rect right next to the first, for clipping
167         m.setTranslate(mSlashRect.width(), 0);
168         mPath.transform(m);
169         mPath.addRoundRect(mSlashRect, 1.0f * width, 1.0f * height, Direction.CW);
170         m.setRotate(mRotation + DEFAULT_ROTATION, width / 2, height / 2);
171         mPath.transform(m);
172         canvas.clipOutPath(mPath);
173 
174         mDrawable.draw(canvas);
175         canvas.restore();
176     }
177 
scale(float frac, int width)178     private float scale(float frac, int width) {
179         return frac * width;
180     }
181 
updateRect(float left, float top, float right, float bottom)182     private void updateRect(float left, float top, float right, float bottom) {
183         mSlashRect.left = left;
184         mSlashRect.top = top;
185         mSlashRect.right = right;
186         mSlashRect.bottom = bottom;
187     }
188 
189     @Override
setTint(@olorInt int tintColor)190     public void setTint(@ColorInt int tintColor) {
191         super.setTint(tintColor);
192         mDrawable.setTint(tintColor);
193         mPaint.setColor(tintColor);
194     }
195 
196     @Override
setTintList(@ullable ColorStateList tint)197     public void setTintList(@Nullable ColorStateList tint) {
198         mTintList = tint;
199         super.setTintList(tint);
200         setDrawableTintList(tint);
201         mPaint.setColor(tint.getDefaultColor());
202         invalidateSelf();
203     }
204 
setDrawableTintList(@ullable ColorStateList tint)205     protected void setDrawableTintList(@Nullable ColorStateList tint) {
206         mDrawable.setTintList(tint);
207     }
208 
209     @Override
setTintMode(@onNull Mode tintMode)210     public void setTintMode(@NonNull Mode tintMode) {
211         mTintMode = tintMode;
212         super.setTintMode(tintMode);
213         mDrawable.setTintMode(tintMode);
214     }
215 
216     @Override
setAlpha(@ntRangefrom = 0, to = 255) int alpha)217     public void setAlpha(@IntRange(from = 0, to = 255) int alpha) {
218         mDrawable.setAlpha(alpha);
219         mPaint.setAlpha(alpha);
220     }
221 
222     @Override
setColorFilter(@ullable ColorFilter colorFilter)223     public void setColorFilter(@Nullable ColorFilter colorFilter) {
224         mDrawable.setColorFilter(colorFilter);
225         mPaint.setColorFilter(colorFilter);
226     }
227 
228     @Override
getOpacity()229     public int getOpacity() {
230         return 255;
231     }
232 }
233