blob: caa51ba2ff83d287dcbbb2f3a14c6aec969b0c54 [file] [log] [blame]
Jorim Jaggibe565df2014-04-28 17:51:23 +02001/*
2 * Copyright (C) 2014 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
17package com.android.systemui.statusbar;
18
19import android.content.Context;
Anthony Chen7acbb772017-04-07 16:45:25 -070020import android.content.res.Resources;
Selim Cinek0fe07392017-11-09 13:26:34 -080021import android.graphics.Canvas;
Jorim Jaggibe565df2014-04-28 17:51:23 +020022import android.graphics.Outline;
Selim Cinek0fe07392017-11-09 13:26:34 -080023import android.graphics.Path;
Chris Craik7b7ca3c2014-07-11 13:32:19 -070024import android.graphics.Rect;
Selim Cinek8efa6dd2014-05-19 16:27:37 +020025import android.graphics.RectF;
Jorim Jaggibe565df2014-04-28 17:51:23 +020026import android.util.AttributeSet;
Chris Craik7b7ca3c2014-07-11 13:32:19 -070027import android.view.View;
28import android.view.ViewOutlineProvider;
Selim Cinek0fe07392017-11-09 13:26:34 -080029
30import com.android.settingslib.Utils;
Anthony Chen7acbb772017-04-07 16:45:25 -070031import com.android.systemui.R;
Selim Cinekd9b7dd42017-11-10 17:53:47 -080032import com.android.systemui.statusbar.notification.AnimatableProperty;
33import com.android.systemui.statusbar.notification.PropertyAnimator;
34import com.android.systemui.statusbar.stack.AnimationProperties;
35import com.android.systemui.statusbar.stack.StackStateAnimator;
Jorim Jaggibe565df2014-04-28 17:51:23 +020036
37/**
38 * Like {@link ExpandableView}, but setting an outline for the height and clipping.
39 */
40public abstract class ExpandableOutlineView extends ExpandableView {
41
Selim Cinekd9b7dd42017-11-10 17:53:47 -080042 private static final AnimatableProperty TOP_ROUNDNESS = AnimatableProperty.from(
43 "topRoundness",
44 ExpandableOutlineView::setTopRoundnessInternal,
45 ExpandableOutlineView::getCurrentTopRoundness,
46 R.id.top_roundess_animator_tag,
47 R.id.top_roundess_animator_end_tag,
48 R.id.top_roundess_animator_start_tag);
49 private static final AnimatableProperty BOTTOM_ROUNDNESS = AnimatableProperty.from(
50 "bottomRoundness",
51 ExpandableOutlineView::setBottomRoundnessInternal,
52 ExpandableOutlineView::getCurrentBottomRoundness,
53 R.id.bottom_roundess_animator_tag,
54 R.id.bottom_roundess_animator_end_tag,
55 R.id.bottom_roundess_animator_start_tag);
56 private static final AnimationProperties ROUNDNESS_PROPERTIES =
57 new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
Selim Cinek515b2032017-11-15 10:20:19 -080058 private static final Path EMPTY_PATH = new Path();
Selim Cinekd9b7dd42017-11-10 17:53:47 -080059
Chris Craik7b7ca3c2014-07-11 13:32:19 -070060 private final Rect mOutlineRect = new Rect();
Selim Cinekb0ee18f2017-12-21 16:15:53 -080061 private final Path mClipPath = new Path();
Selim Cinek8efa6dd2014-05-19 16:27:37 +020062 private boolean mCustomOutline;
Selim Cinekd35c2792016-01-21 13:20:57 -080063 private float mOutlineAlpha = -1f;
Selim Cinekc25989e2018-02-16 16:42:14 -080064 protected float mOutlineRadius;
Selim Cinek0fe07392017-11-09 13:26:34 -080065 private boolean mAlwaysRoundBothCorners;
66 private Path mTmpPath = new Path();
67 private Path mTmpPath2 = new Path();
Selim Cinekd9b7dd42017-11-10 17:53:47 -080068 private float mCurrentBottomRoundness;
69 private float mCurrentTopRoundness;
70 private float mBottomRoundness;
Selim Cinek0fe07392017-11-09 13:26:34 -080071 private float mTopRoundness;
Selim Cinek515b2032017-11-15 10:20:19 -080072 private int mBackgroundTop;
Jorim Jaggibe565df2014-04-28 17:51:23 +020073
Anthony Chen7acbb772017-04-07 16:45:25 -070074 /**
75 * {@code true} if the children views of the {@link ExpandableOutlineView} are translated when
76 * it is moved. Otherwise, the translation is set on the {@code ExpandableOutlineView} itself.
77 */
78 protected boolean mShouldTranslateContents;
Tony Huangc092c432018-05-18 17:38:54 +080079 private boolean mTopAmountRounded;
Selim Cinekb0ee18f2017-12-21 16:15:53 -080080 private float mDistanceToTopRoundness = -1;
Anthony Chen7acbb772017-04-07 16:45:25 -070081
82 private final ViewOutlineProvider mProvider = new ViewOutlineProvider() {
Mady Mellorb0a82462016-04-30 17:31:02 -070083 @Override
84 public void getOutline(View view, Outline outline) {
Selim Cinek1a891a92017-12-04 17:41:27 +010085 if (!mCustomOutline && mCurrentTopRoundness == 0.0f
Tony Huangc092c432018-05-18 17:38:54 +080086 && mCurrentBottomRoundness == 0.0f && !mAlwaysRoundBothCorners
87 && !mTopAmountRounded) {
Selim Cinek1a891a92017-12-04 17:41:27 +010088 int translation = mShouldTranslateContents ? (int) getTranslation() : 0;
Selim Cinekb95fd182017-12-21 13:03:32 -080089 int left = Math.max(translation, 0);
Selim Cinek1a891a92017-12-04 17:41:27 +010090 int top = mClipTopAmount + mBackgroundTop;
Selim Cinekb95fd182017-12-21 13:03:32 -080091 int right = getWidth() + Math.min(translation, 0);
Selim Cinek1a891a92017-12-04 17:41:27 +010092 int bottom = Math.max(getActualHeight() - mClipBottomAmount, top);
93 outline.setRect(left, top, right, bottom);
94 } else {
95 Path clipPath = getClipPath();
96 if (clipPath != null && clipPath.isConvex()) {
97 // The path might not be convex in border cases where the view is small and
98 // clipped
99 outline.setConvexPath(clipPath);
100 }
Mady Mellorb0a82462016-04-30 17:31:02 -0700101 }
102 outline.setAlpha(mOutlineAlpha);
103 }
104 };
105
Selim Cinek0fe07392017-11-09 13:26:34 -0800106 private Path getClipPath() {
Selim Cinek2871bef2017-11-22 08:40:00 -0800107 return getClipPath(false, /* ignoreTranslation */
108 false /* clipRoundedToBottom */);
Selim Cinek515b2032017-11-15 10:20:19 -0800109 }
110
Selim Cinek2871bef2017-11-22 08:40:00 -0800111 protected Path getClipPath(boolean ignoreTranslation, boolean clipRoundedToBottom) {
Selim Cinek0fe07392017-11-09 13:26:34 -0800112 int left;
113 int top;
114 int right;
115 int bottom;
116 int height;
117 Path intersectPath = null;
118 if (!mCustomOutline) {
Selim Cinek515b2032017-11-15 10:20:19 -0800119 int translation = mShouldTranslateContents && !ignoreTranslation
120 ? (int) getTranslation() : 0;
Selim Cinekffd3dc62018-11-30 18:06:57 -0800121 int halfExtraWidth = (int) (mExtraWidthForClipping / 2.0f);
122 left = Math.max(translation, 0) - halfExtraWidth;
Selim Cinek515b2032017-11-15 10:20:19 -0800123 top = mClipTopAmount + mBackgroundTop;
Selim Cinekffd3dc62018-11-30 18:06:57 -0800124 right = getWidth() + halfExtraWidth + Math.min(translation, 0);
Selim Cinek0fe07392017-11-09 13:26:34 -0800125 bottom = Math.max(getActualHeight(), top);
Selim Cinekffd3dc62018-11-30 18:06:57 -0800126 int intersectBottom = Math.max(mMinimumHeightForClipping,
127 Math.max(getActualHeight() - mClipBottomAmount, top));
Selim Cinek0fe07392017-11-09 13:26:34 -0800128 if (bottom != intersectBottom) {
Selim Cinek2871bef2017-11-22 08:40:00 -0800129 if (clipRoundedToBottom) {
130 bottom = intersectBottom;
131 } else {
132 getRoundedRectPath(left, top, right,
133 intersectBottom, 0.0f,
134 0.0f, mTmpPath2);
135 intersectPath = mTmpPath2;
136 }
Selim Cinek0fe07392017-11-09 13:26:34 -0800137 }
138 } else {
139 left = mOutlineRect.left;
140 top = mOutlineRect.top;
141 right = mOutlineRect.right;
142 bottom = mOutlineRect.bottom;
143 }
144 height = bottom - top;
145 if (height == 0) {
Selim Cinek515b2032017-11-15 10:20:19 -0800146 return EMPTY_PATH;
Selim Cinek0fe07392017-11-09 13:26:34 -0800147 }
148 float topRoundness = mAlwaysRoundBothCorners
Tony Huangc092c432018-05-18 17:38:54 +0800149 ? mOutlineRadius : getCurrentBackgroundRadiusTop();
Selim Cinek0fe07392017-11-09 13:26:34 -0800150 float bottomRoundness = mAlwaysRoundBothCorners
Tony Huangc092c432018-05-18 17:38:54 +0800151 ? mOutlineRadius : getCurrentBackgroundRadiusBottom();
Selim Cinek0fe07392017-11-09 13:26:34 -0800152 if (topRoundness + bottomRoundness > height) {
153 float overShoot = topRoundness + bottomRoundness - height;
Selim Cinekd9b7dd42017-11-10 17:53:47 -0800154 topRoundness -= overShoot * mCurrentTopRoundness
155 / (mCurrentTopRoundness + mCurrentBottomRoundness);
156 bottomRoundness -= overShoot * mCurrentBottomRoundness
157 / (mCurrentTopRoundness + mCurrentBottomRoundness);
Selim Cinek0fe07392017-11-09 13:26:34 -0800158 }
159 getRoundedRectPath(left, top, right, bottom, topRoundness,
160 bottomRoundness, mTmpPath);
161 Path roundedRectPath = mTmpPath;
162 if (intersectPath != null) {
163 roundedRectPath.op(intersectPath, Path.Op.INTERSECT);
164 }
165 return roundedRectPath;
166 }
167
Selim Cinekb0ee18f2017-12-21 16:15:53 -0800168 public static void getRoundedRectPath(int left, int top, int right, int bottom,
169 float topRoundness, float bottomRoundness, Path outPath) {
Selim Cinek0fe07392017-11-09 13:26:34 -0800170 outPath.reset();
171 int width = right - left;
172 float topRoundnessX = topRoundness;
173 float bottomRoundnessX = bottomRoundness;
174 topRoundnessX = Math.min(width / 2, topRoundnessX);
175 bottomRoundnessX = Math.min(width / 2, bottomRoundnessX);
176 if (topRoundness > 0.0f) {
177 outPath.moveTo(left, top + topRoundness);
178 outPath.quadTo(left, top, left + topRoundnessX, top);
179 outPath.lineTo(right - topRoundnessX, top);
180 outPath.quadTo(right, top, right, top + topRoundness);
181 } else {
182 outPath.moveTo(left, top);
183 outPath.lineTo(right, top);
184 }
185 if (bottomRoundness > 0.0f) {
186 outPath.lineTo(right, bottom - bottomRoundness);
187 outPath.quadTo(right, bottom, right - bottomRoundnessX, bottom);
188 outPath.lineTo(left + bottomRoundnessX, bottom);
189 outPath.quadTo(left, bottom, left, bottom - bottomRoundness);
190 } else {
191 outPath.lineTo(right, bottom);
192 outPath.lineTo(left, bottom);
193 }
194 outPath.close();
195 }
196
Jorim Jaggibe565df2014-04-28 17:51:23 +0200197 public ExpandableOutlineView(Context context, AttributeSet attrs) {
198 super(context, attrs);
Mady Mellorb0a82462016-04-30 17:31:02 -0700199 setOutlineProvider(mProvider);
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700200 initDimens();
201 }
202
Selim Cinek0fe07392017-11-09 13:26:34 -0800203 @Override
Selim Cinek515b2032017-11-15 10:20:19 -0800204 protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
Selim Cinek0fe07392017-11-09 13:26:34 -0800205 canvas.save();
Selim Cinekb0ee18f2017-12-21 16:15:53 -0800206 Path intersectPath = null;
Tony Huangc092c432018-05-18 17:38:54 +0800207 if (mTopAmountRounded && topAmountNeedsClipping()) {
Selim Cinekc25989e2018-02-16 16:42:14 -0800208 int left = (int) (- mExtraWidthForClipping / 2.0f);
Selim Cinekb0ee18f2017-12-21 16:15:53 -0800209 int top = (int) (mClipTopAmount - mDistanceToTopRoundness);
Selim Cinekc25989e2018-02-16 16:42:14 -0800210 int right = getWidth() + (int) (mExtraWidthForClipping + left);
211 int bottom = (int) Math.max(mMinimumHeightForClipping,
212 Math.max(getActualHeight() - mClipBottomAmount, top + mOutlineRadius));
Selim Cinekb0ee18f2017-12-21 16:15:53 -0800213 ExpandableOutlineView.getRoundedRectPath(left, top, right, bottom, mOutlineRadius,
214 0.0f,
215 mClipPath);
216 intersectPath = mClipPath;
217 }
218 boolean clipped = false;
Selim Cinek515b2032017-11-15 10:20:19 -0800219 if (childNeedsClipping(child)) {
220 Path clipPath = getCustomClipPath(child);
Selim Cinek0fe07392017-11-09 13:26:34 -0800221 if (clipPath == null) {
222 clipPath = getClipPath();
223 }
224 if (clipPath != null) {
Selim Cinekb0ee18f2017-12-21 16:15:53 -0800225 if (intersectPath != null) {
226 clipPath.op(intersectPath, Path.Op.INTERSECT);
227 }
Selim Cinek0fe07392017-11-09 13:26:34 -0800228 canvas.clipPath(clipPath);
Selim Cinekb0ee18f2017-12-21 16:15:53 -0800229 clipped = true;
Selim Cinek0fe07392017-11-09 13:26:34 -0800230 }
231 }
Selim Cinekb0ee18f2017-12-21 16:15:53 -0800232 if (!clipped && intersectPath != null) {
233 canvas.clipPath(intersectPath);
234 }
Selim Cinek515b2032017-11-15 10:20:19 -0800235 boolean result = super.drawChild(canvas, child, drawingTime);
Selim Cinek0fe07392017-11-09 13:26:34 -0800236 canvas.restore();
Selim Cinek515b2032017-11-15 10:20:19 -0800237 return result;
Selim Cinek0fe07392017-11-09 13:26:34 -0800238 }
239
Selim Cinekffd3dc62018-11-30 18:06:57 -0800240 @Override
Selim Cinekc25989e2018-02-16 16:42:14 -0800241 public void setExtraWidthForClipping(float extraWidthForClipping) {
Selim Cinekffd3dc62018-11-30 18:06:57 -0800242 super.setExtraWidthForClipping(extraWidthForClipping);
243 invalidate();
Selim Cinekc25989e2018-02-16 16:42:14 -0800244 }
245
Selim Cinekffd3dc62018-11-30 18:06:57 -0800246 @Override
Selim Cinekc25989e2018-02-16 16:42:14 -0800247 public void setMinimumHeightForClipping(int minimumHeightForClipping) {
Selim Cinekffd3dc62018-11-30 18:06:57 -0800248 super.setMinimumHeightForClipping(minimumHeightForClipping);
249 invalidate();
Selim Cinekc25989e2018-02-16 16:42:14 -0800250 }
251
Selim Cinekb0ee18f2017-12-21 16:15:53 -0800252 @Override
253 public void setDistanceToTopRoundness(float distanceToTopRoundness) {
254 super.setDistanceToTopRoundness(distanceToTopRoundness);
255 if (distanceToTopRoundness != mDistanceToTopRoundness) {
Tony Huangc092c432018-05-18 17:38:54 +0800256 mTopAmountRounded = distanceToTopRoundness >= 0;
Selim Cinekb0ee18f2017-12-21 16:15:53 -0800257 mDistanceToTopRoundness = distanceToTopRoundness;
Tony Huangc092c432018-05-18 17:38:54 +0800258 applyRoundness();
Selim Cinekb0ee18f2017-12-21 16:15:53 -0800259 }
260 }
261
Selim Cinek515b2032017-11-15 10:20:19 -0800262 protected boolean childNeedsClipping(View child) {
Selim Cinek0fe07392017-11-09 13:26:34 -0800263 return false;
264 }
265
Tony Huangc092c432018-05-18 17:38:54 +0800266 public boolean topAmountNeedsClipping() {
267 return true;
268 }
269
Selim Cinek515b2032017-11-15 10:20:19 -0800270 protected boolean isClippingNeeded() {
271 return mAlwaysRoundBothCorners || mCustomOutline || getTranslation() != 0 ;
Selim Cinek515b2032017-11-15 10:20:19 -0800272 }
273
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700274 private void initDimens() {
Anthony Chen7acbb772017-04-07 16:45:25 -0700275 Resources res = getResources();
276 mShouldTranslateContents =
277 res.getBoolean(R.bool.config_translateNotificationContentsOnSwipe);
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700278 mOutlineRadius = res.getDimension(R.dimen.notification_shadow_radius);
Selim Cinek0fe07392017-11-09 13:26:34 -0800279 mAlwaysRoundBothCorners = res.getBoolean(R.bool.config_clipNotificationsToOutline);
280 if (!mAlwaysRoundBothCorners) {
281 mOutlineRadius = res.getDimensionPixelSize(
282 Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
283 }
284 setClipToOutline(mAlwaysRoundBothCorners);
285 }
286
Selim Cinek29aab962018-02-27 17:05:45 -0800287 /**
288 * Set the topRoundness of this view.
289 * @return Whether the roundness was changed.
290 */
291 public boolean setTopRoundness(float topRoundness, boolean animate) {
Selim Cinek0fe07392017-11-09 13:26:34 -0800292 if (mTopRoundness != topRoundness) {
293 mTopRoundness = topRoundness;
Selim Cinekd9b7dd42017-11-10 17:53:47 -0800294 PropertyAnimator.setProperty(this, TOP_ROUNDNESS, topRoundness,
295 ROUNDNESS_PROPERTIES, animate);
Selim Cinek29aab962018-02-27 17:05:45 -0800296 return true;
Selim Cinek0fe07392017-11-09 13:26:34 -0800297 }
Selim Cinek29aab962018-02-27 17:05:45 -0800298 return false;
Selim Cinek0fe07392017-11-09 13:26:34 -0800299 }
300
301 protected void applyRoundness() {
302 invalidateOutline();
303 invalidate();
304 }
305
Selim Cinek515b2032017-11-15 10:20:19 -0800306 public float getCurrentBackgroundRadiusTop() {
Tony Huangc092c432018-05-18 17:38:54 +0800307 // If this view is top amount notification view, it should always has round corners on top.
308 // It will be applied with applyRoundness()
309 if (mTopAmountRounded) {
310 return mOutlineRadius;
311 }
Selim Cinekd9b7dd42017-11-10 17:53:47 -0800312 return mCurrentTopRoundness * mOutlineRadius;
Selim Cinek0fe07392017-11-09 13:26:34 -0800313 }
314
Selim Cinek515b2032017-11-15 10:20:19 -0800315 public float getCurrentTopRoundness() {
Selim Cinekd9b7dd42017-11-10 17:53:47 -0800316 return mCurrentTopRoundness;
Selim Cinek0fe07392017-11-09 13:26:34 -0800317 }
318
Selim Cinek2871bef2017-11-22 08:40:00 -0800319 public float getCurrentBottomRoundness() {
Selim Cinekd9b7dd42017-11-10 17:53:47 -0800320 return mCurrentBottomRoundness;
Selim Cinek0fe07392017-11-09 13:26:34 -0800321 }
322
Selim Cinekd9b7dd42017-11-10 17:53:47 -0800323 protected float getCurrentBackgroundRadiusBottom() {
324 return mCurrentBottomRoundness * mOutlineRadius;
325 }
326
Selim Cinek29aab962018-02-27 17:05:45 -0800327 /**
328 * Set the bottom roundness of this view.
329 * @return Whether the roundness was changed.
330 */
331 public boolean setBottomRoundness(float bottomRoundness, boolean animate) {
Selim Cinekd9b7dd42017-11-10 17:53:47 -0800332 if (mBottomRoundness != bottomRoundness) {
333 mBottomRoundness = bottomRoundness;
334 PropertyAnimator.setProperty(this, BOTTOM_ROUNDNESS, bottomRoundness,
335 ROUNDNESS_PROPERTIES, animate);
Selim Cinek29aab962018-02-27 17:05:45 -0800336 return true;
Selim Cinek0fe07392017-11-09 13:26:34 -0800337 }
Selim Cinek29aab962018-02-27 17:05:45 -0800338 return false;
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700339 }
340
Selim Cinek515b2032017-11-15 10:20:19 -0800341 protected void setBackgroundTop(int backgroundTop) {
342 if (mBackgroundTop != backgroundTop) {
343 mBackgroundTop = backgroundTop;
344 invalidateOutline();
345 }
346 }
Selim Cinekd9b7dd42017-11-10 17:53:47 -0800347
348 private void setTopRoundnessInternal(float topRoundness) {
349 mCurrentTopRoundness = topRoundness;
350 applyRoundness();
351 }
352
353 private void setBottomRoundnessInternal(float bottomRoundness) {
354 mCurrentBottomRoundness = bottomRoundness;
355 applyRoundness();
356 }
357
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700358 public void onDensityOrFontScaleChanged() {
359 initDimens();
Selim Cinek0fe07392017-11-09 13:26:34 -0800360 applyRoundness();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200361 }
362
363 @Override
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200364 public void setActualHeight(int actualHeight, boolean notifyListeners) {
Selim Cinek0fe07392017-11-09 13:26:34 -0800365 int previousHeight = getActualHeight();
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200366 super.setActualHeight(actualHeight, notifyListeners);
Selim Cinek0fe07392017-11-09 13:26:34 -0800367 if (previousHeight != actualHeight) {
368 applyRoundness();
369 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200370 }
371
372 @Override
373 public void setClipTopAmount(int clipTopAmount) {
Selim Cinek0fe07392017-11-09 13:26:34 -0800374 int previousAmount = getClipTopAmount();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200375 super.setClipTopAmount(clipTopAmount);
Selim Cinek0fe07392017-11-09 13:26:34 -0800376 if (previousAmount != clipTopAmount) {
377 applyRoundness();
378 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200379 }
380
Selim Cinekb3dadcc2016-11-21 17:21:13 -0800381 @Override
382 public void setClipBottomAmount(int clipBottomAmount) {
Selim Cinek0fe07392017-11-09 13:26:34 -0800383 int previousAmount = getClipBottomAmount();
Selim Cinekb3dadcc2016-11-21 17:21:13 -0800384 super.setClipBottomAmount(clipBottomAmount);
Selim Cinek0fe07392017-11-09 13:26:34 -0800385 if (previousAmount != clipBottomAmount) {
386 applyRoundness();
387 }
Selim Cinekb3dadcc2016-11-21 17:21:13 -0800388 }
389
Jorim Jaggi83eb6bb2015-08-17 17:38:58 -0700390 protected void setOutlineAlpha(float alpha) {
Selim Cinekd35c2792016-01-21 13:20:57 -0800391 if (alpha != mOutlineAlpha) {
392 mOutlineAlpha = alpha;
Selim Cinek0fe07392017-11-09 13:26:34 -0800393 applyRoundness();
Selim Cinekd35c2792016-01-21 13:20:57 -0800394 }
Jorim Jaggi83eb6bb2015-08-17 17:38:58 -0700395 }
396
Selim Cinek33223572016-02-19 19:32:22 -0800397 @Override
398 public float getOutlineAlpha() {
399 return mOutlineAlpha;
400 }
401
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200402 protected void setOutlineRect(RectF rect) {
403 if (rect != null) {
404 setOutlineRect(rect.left, rect.top, rect.right, rect.bottom);
405 } else {
406 mCustomOutline = false;
Selim Cinek0fe07392017-11-09 13:26:34 -0800407 applyRoundness();
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200408 }
409 }
410
Selim Cinek33223572016-02-19 19:32:22 -0800411 @Override
412 public int getOutlineTranslation() {
Mady Mellorb0a82462016-04-30 17:31:02 -0700413 return mCustomOutline ? mOutlineRect.left : (int) getTranslation();
414 }
415
416 public void updateOutline() {
417 if (mCustomOutline) {
418 return;
419 }
Selim Cinekad7fac02016-10-18 17:09:15 -0700420 boolean hasOutline = needsOutline();
Mady Mellorb0a82462016-04-30 17:31:02 -0700421 setOutlineProvider(hasOutline ? mProvider : null);
422 }
423
Selim Cinekad7fac02016-10-18 17:09:15 -0700424 /**
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700425 * @return Whether the view currently needs an outline. This is usually {@code false} in case
426 * it doesn't have a background.
Selim Cinekad7fac02016-10-18 17:09:15 -0700427 */
428 protected boolean needsOutline() {
429 if (isChildInGroup()) {
430 return isGroupExpanded() && !isGroupExpansionChanging();
431 } else if (isSummaryWithChildren()) {
432 return !isGroupExpanded() || isGroupExpansionChanging();
433 }
434 return true;
435 }
436
Mady Mellorb0a82462016-04-30 17:31:02 -0700437 public boolean isOutlineShowing() {
438 ViewOutlineProvider op = getOutlineProvider();
439 return op != null;
Selim Cinek33223572016-02-19 19:32:22 -0800440 }
441
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200442 protected void setOutlineRect(float left, float top, float right, float bottom) {
443 mCustomOutline = true;
444
Chris Craik7b7ca3c2014-07-11 13:32:19 -0700445 mOutlineRect.set((int) left, (int) top, (int) right, (int) bottom);
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200446
447 // Outlines need to be at least 1 dp
Selim Cinek3969ffa2014-09-01 23:08:39 +0200448 mOutlineRect.bottom = (int) Math.max(top, mOutlineRect.bottom);
449 mOutlineRect.right = (int) Math.max(left, mOutlineRect.right);
Selim Cinek0fe07392017-11-09 13:26:34 -0800450 applyRoundness();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200451 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200452
Selim Cinek515b2032017-11-15 10:20:19 -0800453 public Path getCustomClipPath(View child) {
Selim Cinek0fe07392017-11-09 13:26:34 -0800454 return null;
455 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200456}