blob: 5391b4d4654db6023fae6542d6a3568a187f97b1 [file] [log] [blame]
Sunny Goyal4fe5a372015-05-14 19:55:10 -07001/*
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.launcher3;
18
Sunny Goyalea529082017-10-31 13:33:03 -070019import static com.android.launcher3.FastBitmapDrawable.CLICK_FEEDBACK_DURATION;
20import static com.android.launcher3.FastBitmapDrawable.CLICK_FEEDBACK_INTERPOLATOR;
21import static com.android.launcher3.LauncherAnimUtils.ELEVATION;
22import static com.android.launcher3.graphics.HolographicOutlineHelper.ADAPTIVE_ICON_SHADOW_BITMAP;
23
24import android.animation.ObjectAnimator;
25import android.annotation.TargetApi;
Sunny Goyal4fe5a372015-05-14 19:55:10 -070026import android.content.Context;
27import android.graphics.Bitmap;
28import android.graphics.Canvas;
29import android.graphics.Color;
Sunny Goyalea529082017-10-31 13:33:03 -070030import android.graphics.Outline;
Sunny Goyal4fe5a372015-05-14 19:55:10 -070031import android.graphics.Paint;
Winsonbe9798b2016-07-20 12:55:49 -070032import android.graphics.Rect;
Sunny Goyalea529082017-10-31 13:33:03 -070033import android.graphics.drawable.AdaptiveIconDrawable;
34import android.graphics.drawable.Drawable;
35import android.os.Build;
36import android.util.Property;
Sunny Goyal4fe5a372015-05-14 19:55:10 -070037import android.view.View;
Sunny Goyal4ffec482016-02-09 11:28:52 -080038import android.view.ViewDebug;
Sunny Goyal4fe5a372015-05-14 19:55:10 -070039import android.view.ViewGroup;
Sunny Goyalea529082017-10-31 13:33:03 -070040import android.view.ViewOutlineProvider;
Sunny Goyal4fe5a372015-05-14 19:55:10 -070041
42public class ClickShadowView extends View {
43
44 private static final int SHADOW_SIZE_FACTOR = 3;
45 private static final int SHADOW_LOW_ALPHA = 30;
46 private static final int SHADOW_HIGH_ALPHA = 60;
47
Sunny Goyalea529082017-10-31 13:33:03 -070048 private static float sAdaptiveIconScaleFactor = 1f;
49
Sunny Goyal4fe5a372015-05-14 19:55:10 -070050 private final Paint mPaint;
51
Sunny Goyal4ffec482016-02-09 11:28:52 -080052 @ViewDebug.ExportedProperty(category = "launcher")
Sunny Goyal4fe5a372015-05-14 19:55:10 -070053 private final float mShadowOffset;
Sunny Goyal4ffec482016-02-09 11:28:52 -080054 @ViewDebug.ExportedProperty(category = "launcher")
Sunny Goyal4fe5a372015-05-14 19:55:10 -070055 private final float mShadowPadding;
56
57 private Bitmap mBitmap;
Sunny Goyalea529082017-10-31 13:33:03 -070058 private ObjectAnimator mAnim;
59
60 private Drawable mAdaptiveIcon;
61 private ViewOutlineProvider mOutlineProvider;
Sunny Goyal4fe5a372015-05-14 19:55:10 -070062
63 public ClickShadowView(Context context) {
64 super(context);
65 mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
66 mPaint.setColor(Color.BLACK);
67
68 mShadowPadding = getResources().getDimension(R.dimen.blur_size_click_shadow);
69 mShadowOffset = getResources().getDimension(R.dimen.click_shadow_high_shift);
70 }
71
Sunny Goyalea529082017-10-31 13:33:03 -070072 public static void setAdaptiveIconScaleFactor(float factor) {
73 sAdaptiveIconScaleFactor = factor;
74 }
75
Sunny Goyal4fe5a372015-05-14 19:55:10 -070076 /**
77 * @return extra space required by the view to show the shadow.
78 */
79 public int getExtraSize() {
80 return (int) (SHADOW_SIZE_FACTOR * mShadowPadding);
81 }
82
Sunny Goyalea529082017-10-31 13:33:03 -070083 public void setPressedIcon(BubbleTextView icon, Bitmap background) {
84 if (icon == null) {
85 setBitmap(null);
86 cancelAnim();
87 return;
88 }
89 if (background == null) {
90 if (mBitmap == ADAPTIVE_ICON_SHADOW_BITMAP) {
91 // clear animation shadow
92 }
93 setBitmap(null);
94 cancelAnim();
95 icon.setOutlineProvider(null);
96 } else if (setBitmap(background)) {
97 if (mBitmap == ADAPTIVE_ICON_SHADOW_BITMAP) {
98 setupAdaptiveShadow(icon);
99 cancelAnim();
100 startAnim(icon, ELEVATION,
101 getResources().getDimension(R.dimen.click_shadow_elevation));
102 } else {
103 alignWithIconView(icon);
104 startAnim(this, ALPHA, 1);
105 }
106 }
107 }
108
109 @TargetApi(Build.VERSION_CODES.O)
110 private void setupAdaptiveShadow(final BubbleTextView view) {
111 if (mAdaptiveIcon == null) {
112 mAdaptiveIcon = new AdaptiveIconDrawable(null, null);
113 mOutlineProvider = new ViewOutlineProvider() {
114 @Override
115 public void getOutline(View view, Outline outline) {
116 mAdaptiveIcon.getOutline(outline);
117 }
118 };
119 }
120
121 int iconWidth = view.getRight() - view.getLeft();
122 int iconHSpace = iconWidth - view.getCompoundPaddingRight() - view.getCompoundPaddingLeft();
123 int drawableWidth = view.getIcon().getBounds().width();
124
125 Rect bounds = new Rect();
126 bounds.left = view.getCompoundPaddingLeft() + (iconHSpace - drawableWidth) / 2;
127 bounds.right = bounds.left + drawableWidth;
128 bounds.top = view.getPaddingTop();
129 bounds.bottom = bounds.top + view.getIcon().getBounds().height();
130 Utilities.scaleRectAboutCenter(bounds, sAdaptiveIconScaleFactor);
131
132 mAdaptiveIcon.setBounds(bounds);
133 view.setOutlineProvider(mOutlineProvider);
134 }
135
Sunny Goyal4fe5a372015-05-14 19:55:10 -0700136 /**
137 * Applies the new bitmap.
138 * @return true if the view was invalidated.
139 */
Sunny Goyalea529082017-10-31 13:33:03 -0700140 private boolean setBitmap(Bitmap b) {
Sunny Goyal4fe5a372015-05-14 19:55:10 -0700141 if (b != mBitmap){
142 mBitmap = b;
143 invalidate();
144 return true;
145 }
146 return false;
147 }
148
149 @Override
150 protected void onDraw(Canvas canvas) {
151 if (mBitmap != null) {
152 mPaint.setAlpha(SHADOW_LOW_ALPHA);
153 canvas.drawBitmap(mBitmap, 0, 0, mPaint);
154 mPaint.setAlpha(SHADOW_HIGH_ALPHA);
155 canvas.drawBitmap(mBitmap, 0, mShadowOffset, mPaint);
156 }
157 }
158
Sunny Goyalea529082017-10-31 13:33:03 -0700159 private void cancelAnim() {
160 if (mAnim != null) {
161 mAnim.cancel();
162 mAnim.setCurrentPlayTime(0);
163 mAnim = null;
164 }
165 }
166
167 private void startAnim(View target, Property<View, Float> property, float endValue) {
168 cancelAnim();
169 property.set(target, 0f);
170 mAnim = ObjectAnimator.ofFloat(target, property, endValue);
171 mAnim.setDuration(CLICK_FEEDBACK_DURATION)
172 .setInterpolator(CLICK_FEEDBACK_INTERPOLATOR);
173 mAnim.start();
Sunny Goyal4fe5a372015-05-14 19:55:10 -0700174 }
175
176 /**
177 * Aligns the shadow with {@param view}
Sunny Goyalea529082017-10-31 13:33:03 -0700178 * Note: {@param view} must be a descendant of my parent.
Sunny Goyal4fe5a372015-05-14 19:55:10 -0700179 */
Sunny Goyalea529082017-10-31 13:33:03 -0700180 private void alignWithIconView(BubbleTextView view) {
181 int[] coords = new int[] {0, 0};
182 Utilities.getDescendantCoordRelativeToAncestor(
183 (ViewGroup) view.getParent(), (View) getParent(), coords, false);
184
185 float leftShift = view.getLeft() + coords[0] - getLeft();
186 float topShift = view.getTop() + coords[1] - getTop();
Sunny Goyal4fe5a372015-05-14 19:55:10 -0700187 int iconWidth = view.getRight() - view.getLeft();
Winsonbe9798b2016-07-20 12:55:49 -0700188 int iconHeight = view.getBottom() - view.getTop();
Sunny Goyal4fe5a372015-05-14 19:55:10 -0700189 int iconHSpace = iconWidth - view.getCompoundPaddingRight() - view.getCompoundPaddingLeft();
190 float drawableWidth = view.getIcon().getBounds().width();
191
Sunny Goyalea529082017-10-31 13:33:03 -0700192 // Set the bounds to clip against
193 int clipLeft = (int) Math.max(0, coords[0] - leftShift - mShadowPadding);
194 int clipTop = (int) Math.max(0, coords[1] - topShift - mShadowPadding) ;
195 setClipBounds(new Rect(clipLeft, clipTop, clipLeft + iconWidth, clipTop + iconHeight));
Winsonbe9798b2016-07-20 12:55:49 -0700196
Sunny Goyal4fe5a372015-05-14 19:55:10 -0700197 setTranslationX(leftShift
198 + view.getCompoundPaddingLeft() * view.getScaleX()
199 + (iconHSpace - drawableWidth) * view.getScaleX() / 2 /* drawable gap */
200 + iconWidth * (1 - view.getScaleX()) / 2 /* gap due to scale */
201 - mShadowPadding /* extra shadow size */
202 );
203 setTranslationY(topShift
Sunny Goyal4fe5a372015-05-14 19:55:10 -0700204 + view.getPaddingTop() * view.getScaleY() /* drawable gap */
205 + view.getHeight() * (1 - view.getScaleY()) / 2 /* gap due to scale */
206 - mShadowPadding /* extra shadow size */
207 );
208 }
209}