blob: e5a4335dcd6cde8e177c0adf442f977a3c03b8aa [file] [log] [blame]
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001/*
2 * Copyright (C) 2008 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
Daniel Sandler325dc232013-06-05 22:57:57 -040017package com.android.launcher3;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080018
Sunny Goyal14168432019-10-24 15:59:49 -070019import static com.android.launcher3.FastBitmapDrawable.newIcon;
Jon Miranda228877d2021-02-09 11:05:00 -050020import static com.android.launcher3.config.FeatureFlags.ENABLE_FOUR_COLUMNS;
Samuel Fufa314761a2020-10-14 10:15:07 -070021import static com.android.launcher3.graphics.IconShape.getShape;
Sunny Goyal14168432019-10-24 15:59:49 -070022import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
Sunny Goyal066ace12018-11-05 11:08:31 -080023import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
24
Tony4043b052018-08-17 13:47:39 -070025import android.animation.Animator;
26import android.animation.AnimatorListenerAdapter;
Tony Wickham1237df02017-02-24 08:59:36 -080027import android.animation.ObjectAnimator;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080028import android.content.Context;
Adam Cohen96bb7982014-07-07 11:58:56 -070029import android.content.res.ColorStateList;
Adam Cohen96bb7982014-07-07 11:58:56 -070030import android.content.res.TypedArray;
Samuel Fufa314761a2020-10-14 10:15:07 -070031import android.graphics.BlurMaskFilter;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080032import android.graphics.Canvas;
Tony17b7f9b2017-05-23 12:19:09 -070033import android.graphics.Color;
Winson1f064272016-07-18 17:18:02 -070034import android.graphics.Paint;
Samuel Fufa314761a2020-10-14 10:15:07 -070035import android.graphics.Path;
Adam Cohend9162062020-03-24 16:35:35 -070036import android.graphics.PointF;
Tony Wickham1237df02017-02-24 08:59:36 -080037import android.graphics.Rect;
Tony17b7f9b2017-05-23 12:19:09 -070038import android.graphics.drawable.ColorDrawable;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080039import android.graphics.drawable.Drawable;
Samuel Fufa314761a2020-10-14 10:15:07 -070040import android.os.Process;
Sunny Goyal66dccad2018-03-19 12:00:51 -070041import android.text.TextUtils.TruncateAt;
Winson Chung656d11c2010-11-29 17:15:47 -080042import android.util.AttributeSet;
Tony Wickham1237df02017-02-24 08:59:36 -080043import android.util.Property;
Winson Chung5f8afe62013-08-12 16:19:28 -070044import android.util.TypedValue;
Sunny Goyal508da152014-08-14 10:53:27 -070045import android.view.KeyEvent;
Michael Jurka38b4f7c2010-12-14 16:46:39 -080046import android.view.MotionEvent;
Sunny Goyal317698b2015-07-29 11:45:41 -070047import android.view.View;
Sunny Goyal4ffec482016-02-09 11:28:52 -080048import android.view.ViewDebug;
Michael Jurkabdb5c532011-02-01 15:05:06 -080049import android.widget.TextView;
Sunny Goyal317698b2015-07-29 11:45:41 -070050
Schneider Victor-tulias6a5f4312021-01-12 19:30:02 +000051import androidx.annotation.Nullable;
Hyunyoung Song5809a402021-02-02 01:19:18 -080052import androidx.annotation.UiThread;
Schneider Victor-tulias350b4622020-09-18 11:16:30 -070053
Sunny Goyal3dce5f32017-10-05 11:40:05 -070054import com.android.launcher3.Launcher.OnResumeCallback;
Adam Cohen29794c52019-06-21 12:50:30 -070055import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
Samuel Fufa314761a2020-10-14 10:15:07 -070056import com.android.launcher3.allapps.AllAppsSectionDecorator;
57import com.android.launcher3.config.FeatureFlags;
Tony Wickhamf34bee82018-12-03 18:11:39 -080058import com.android.launcher3.dot.DotInfo;
Adam Cohen65086992020-02-19 08:40:49 -080059import com.android.launcher3.dragndrop.DraggableView;
Tony7308cde2017-06-27 22:38:33 -070060import com.android.launcher3.folder.FolderIcon;
Tony Wickham1237df02017-02-24 08:59:36 -080061import com.android.launcher3.graphics.IconPalette;
Tony Wickhame1cb93f2019-05-03 11:27:32 -070062import com.android.launcher3.graphics.IconShape;
Schneider Victor-tulias350b4622020-09-18 11:16:30 -070063import com.android.launcher3.graphics.PlaceHolderIconDrawable;
Sunny Goyal96ac68a2017-02-02 16:37:21 -080064import com.android.launcher3.graphics.PreloadIconDrawable;
Tony Wickham8912b042018-11-29 15:28:53 -080065import com.android.launcher3.icons.DotRenderer;
Sunny Goyalfe8e4a92018-11-13 19:43:57 -080066import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
Samuel Fufa314761a2020-10-14 10:15:07 -070067import com.android.launcher3.icons.LauncherIcons;
Sunny Goyal79e52fc2021-02-03 10:22:28 -080068import com.android.launcher3.icons.cache.HandlerRunnable;
Sunny Goyale396abf2020-04-06 15:11:17 -070069import com.android.launcher3.model.data.AppInfo;
70import com.android.launcher3.model.data.ItemInfo;
71import com.android.launcher3.model.data.ItemInfoWithIcon;
72import com.android.launcher3.model.data.PackageItemInfo;
Samuel Fufac2820182021-01-22 11:47:25 -060073import com.android.launcher3.model.data.SearchActionItemInfo;
Sunny Goyale396abf2020-04-06 15:11:17 -070074import com.android.launcher3.model.data.WorkspaceItemInfo;
Samuel Fufa167210a2020-05-12 18:40:11 -070075import com.android.launcher3.util.SafeCloseable;
Sunny Goyalfe8e4a92018-11-13 19:43:57 -080076import com.android.launcher3.views.ActivityContext;
Jon Miranda9b78e192019-08-30 18:42:01 -070077import com.android.launcher3.views.IconLabelDotView;
Sunny Goyal34b65272015-03-11 16:56:52 -070078
Sunny Goyalc469aad2015-10-01 11:24:23 -070079import java.text.NumberFormat;
80
The Android Open Source Project31dd5032009-03-03 19:32:27 -080081/**
82 * TextView that draws a bubble behind the text. We cannot use a LineBackgroundSpan
83 * because we want to make the bubble taller than the text and TextView's clip is
84 * too aggressive.
85 */
Jon Miranda9b78e192019-08-30 18:42:01 -070086public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, OnResumeCallback,
Samuel Fufa314761a2020-10-14 10:15:07 -070087 IconLabelDotView, DraggableView, Reorderable, AllAppsSectionDecorator.SelfDecoratingView {
Sunny Goyal95abbb32014-08-04 10:53:22 -070088
Sunny Goyaldfaccf62015-05-11 16:30:44 -070089 private static final int DISPLAY_WORKSPACE = 0;
90 private static final int DISPLAY_ALL_APPS = 1;
Sunny Goyalbaec6ff2016-09-14 11:26:21 -070091 private static final int DISPLAY_FOLDER = 2;
Samuel Fufa4a53c702020-08-04 14:06:55 -070092 private static final int DISPLAY_HERO_APP = 5;
Tony Wickham794fe4f2021-01-11 14:54:23 -060093 protected static final int DISPLAY_TASKBAR = 6;
Sunny Goyaldfaccf62015-05-11 16:30:44 -070094
Samuel Fufad6bacdc2020-08-23 21:56:07 -070095 private static final int[] STATE_PRESSED = new int[]{android.R.attr.state_pressed};
Samuel Fufa314761a2020-10-14 10:15:07 -070096 private static final float HIGHLIGHT_SCALE = 1.16f;
97
Sunny Goyal2a76e3f2017-02-16 13:33:15 -080098
Adam Cohen1d13c0b2020-04-21 16:29:12 -070099 private final PointF mTranslationForReorderBounce = new PointF(0, 0);
100 private final PointF mTranslationForReorderPreview = new PointF(0, 0);
101
Schneider Victor-tulias350b4622020-09-18 11:16:30 -0700102 private static final int ICON_UPDATE_ANIMATION_DURATION = 375;
103
Adam Cohen1d13c0b2020-04-21 16:29:12 -0700104 private float mScaleForReorderBounce = 1f;
Tony Wickham1237df02017-02-24 08:59:36 -0800105
Samuel Fufa314761a2020-10-14 10:15:07 -0700106 protected final Paint mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
107 private final Path mHighlightPath = new Path();
108 protected int mHighlightColor = Color.TRANSPARENT;
109 private final BlurMaskFilter mHighlightShadowFilter;
110
Tony Wickhamf34bee82018-12-03 18:11:39 -0800111 private static final Property<BubbleTextView, Float> DOT_SCALE_PROPERTY
112 = new Property<BubbleTextView, Float>(Float.TYPE, "dotScale") {
Tony Wickham1237df02017-02-24 08:59:36 -0800113 @Override
114 public Float get(BubbleTextView bubbleTextView) {
Tony Wickham8912b042018-11-29 15:28:53 -0800115 return bubbleTextView.mDotParams.scale;
Tony Wickham1237df02017-02-24 08:59:36 -0800116 }
117
118 @Override
119 public void set(BubbleTextView bubbleTextView, Float value) {
Tony Wickham8912b042018-11-29 15:28:53 -0800120 bubbleTextView.mDotParams.scale = value;
Tony Wickham1237df02017-02-24 08:59:36 -0800121 bubbleTextView.invalidate();
122 }
123 };
124
Sunny Goyaled7a6932018-04-13 11:41:50 -0700125 public static final Property<BubbleTextView, Float> TEXT_ALPHA_PROPERTY
126 = new Property<BubbleTextView, Float>(Float.class, "textAlpha") {
Tony8f402802017-06-16 17:24:54 -0700127 @Override
Sunny Goyaled7a6932018-04-13 11:41:50 -0700128 public Float get(BubbleTextView bubbleTextView) {
129 return bubbleTextView.mTextAlpha;
Tony8f402802017-06-16 17:24:54 -0700130 }
131
132 @Override
Sunny Goyaled7a6932018-04-13 11:41:50 -0700133 public void set(BubbleTextView bubbleTextView, Float alpha) {
Tony8f402802017-06-16 17:24:54 -0700134 bubbleTextView.setTextAlpha(alpha);
135 }
136 };
137
Sunny Goyalfe8e4a92018-11-13 19:43:57 -0800138 private final ActivityContext mActivity;
Sunny Goyaled7a6932018-04-13 11:41:50 -0700139 private Drawable mIcon;
Adam Cohen65086992020-02-19 08:40:49 -0800140 private boolean mCenterVertically;
Sunny Goyaled7a6932018-04-13 11:41:50 -0700141
Tony Wickham794fe4f2021-01-11 14:54:23 -0600142 protected final int mDisplay;
Samuel Fufac96fa242019-09-26 23:06:32 -0700143
Sunny Goyaled7a6932018-04-13 11:41:50 -0700144 private final CheckLongPressHelper mLongPressHelper;
Sunny Goyaled7a6932018-04-13 11:41:50 -0700145
146 private final boolean mLayoutHorizontal;
147 private final int mIconSize;
148
149 @ViewDebug.ExportedProperty(category = "launcher")
150 private boolean mIsIconVisible = true;
151 @ViewDebug.ExportedProperty(category = "launcher")
152 private int mTextColor;
153 @ViewDebug.ExportedProperty(category = "launcher")
154 private float mTextAlpha = 1;
155
Tony4043b052018-08-17 13:47:39 -0700156 @ViewDebug.ExportedProperty(category = "launcher")
Tony Wickhamf34bee82018-12-03 18:11:39 -0800157 private DotInfo mDotInfo;
Tony Wickham8912b042018-11-29 15:28:53 -0800158 private DotRenderer mDotRenderer;
159 @ViewDebug.ExportedProperty(category = "launcher", deepExport = true)
160 private DotRenderer.DrawParams mDotParams;
Tony Wickhamf34bee82018-12-03 18:11:39 -0800161 private Animator mDotScaleAnim;
162 private boolean mForceHideDot;
Sunny Goyaled7a6932018-04-13 11:41:50 -0700163
Sunny Goyal4ffec482016-02-09 11:28:52 -0800164 @ViewDebug.ExportedProperty(category = "launcher")
Michael Jurkaddd62e92011-02-16 17:49:14 -0800165 private boolean mStayPressed;
Sunny Goyal4ffec482016-02-09 11:28:52 -0800166 @ViewDebug.ExportedProperty(category = "launcher")
Sunny Goyal508da152014-08-14 10:53:27 -0700167 private boolean mIgnorePressedStateChange;
Sunny Goyal4ffec482016-02-09 11:28:52 -0800168 @ViewDebug.ExportedProperty(category = "launcher")
Sunny Goyal69b75642015-05-15 17:00:24 -0700169 private boolean mDisableRelayout = false;
Chris Wrenaeff7ea2014-02-14 16:59:24 -0500170
Sunny Goyal79e52fc2021-02-03 10:22:28 -0800171 private HandlerRunnable mIconLoadRequest;
Sunny Goyal34b65272015-03-11 16:56:52 -0700172
Schneider Victor-tulias7bbec502020-10-29 11:39:24 -0400173 private boolean mEnableIconUpdateAnimation = false;
174
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800175 public BubbleTextView(Context context) {
Adam Cohen96bb7982014-07-07 11:58:56 -0700176 this(context, null, 0);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800177 }
178
179 public BubbleTextView(Context context, AttributeSet attrs) {
Adam Cohen96bb7982014-07-07 11:58:56 -0700180 this(context, attrs, 0);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800181 }
182
183 public BubbleTextView(Context context, AttributeSet attrs, int defStyle) {
184 super(context, attrs, defStyle);
Sunny Goyalfe8e4a92018-11-13 19:43:57 -0800185 mActivity = ActivityContext.lookupContext(context);
Adam Cohen96bb7982014-07-07 11:58:56 -0700186
Adam Cohen96bb7982014-07-07 11:58:56 -0700187 TypedArray a = context.obtainStyledAttributes(attrs,
188 R.styleable.BubbleTextView, defStyle, 0);
Winson Chungb745afb2015-03-02 11:51:23 -0800189 mLayoutHorizontal = a.getBoolean(R.styleable.BubbleTextView_layoutHorizontal, false);
Sunny Goyalc4d32012020-04-03 17:10:11 -0700190 DeviceProfile grid = mActivity.getDeviceProfile();
Sunny Goyaldfaccf62015-05-11 16:30:44 -0700191
Samuel Fufac96fa242019-09-26 23:06:32 -0700192 mDisplay = a.getInteger(R.styleable.BubbleTextView_iconDisplay, DISPLAY_WORKSPACE);
Sunny Goyalae6e3182019-04-30 12:04:37 -0700193 final int defaultIconSize;
Samuel Fufac96fa242019-09-26 23:06:32 -0700194 if (mDisplay == DISPLAY_WORKSPACE) {
Winson Chung44d0aac2015-05-11 18:02:55 -0700195 setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
Jon Miranda09660722017-06-14 14:20:14 -0700196 setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
Sunny Goyalae6e3182019-04-30 12:04:37 -0700197 defaultIconSize = grid.iconSizePx;
Jon Miranda228877d2021-02-09 11:05:00 -0500198 setCenterVertically(ENABLE_FOUR_COLUMNS.get());
Samuel Fufac96fa242019-09-26 23:06:32 -0700199 } else if (mDisplay == DISPLAY_ALL_APPS) {
Winson1f064272016-07-18 17:18:02 -0700200 setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.allAppsIconTextSizePx);
201 setCompoundDrawablePadding(grid.allAppsIconDrawablePaddingPx);
Sunny Goyaldfaccf62015-05-11 16:30:44 -0700202 defaultIconSize = grid.allAppsIconSizePx;
Samuel Fufac96fa242019-09-26 23:06:32 -0700203 } else if (mDisplay == DISPLAY_FOLDER) {
Jon Mirandabf7d8122016-11-03 15:29:29 -0700204 setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.folderChildTextSizePx);
Sunny Goyalbaec6ff2016-09-14 11:26:21 -0700205 setCompoundDrawablePadding(grid.folderChildDrawablePaddingPx);
Jon Mirandabf7d8122016-11-03 15:29:29 -0700206 defaultIconSize = grid.folderChildIconSizePx;
Samuel Fufa4a53c702020-08-04 14:06:55 -0700207 } else if (mDisplay == DISPLAY_HERO_APP) {
208 defaultIconSize = grid.allAppsIconSizePx;
Tony Wickham794fe4f2021-01-11 14:54:23 -0600209 } else if (mDisplay == DISPLAY_TASKBAR) {
210 defaultIconSize = grid.iconSizePx;
Sunny Goyalae6e3182019-04-30 12:04:37 -0700211 } else {
Samuel Fufa4a1d9b12019-09-19 17:28:00 -0700212 // widget_selection or shortcut_popup
Sunny Goyalc4d32012020-04-03 17:10:11 -0700213 defaultIconSize = grid.iconSizePx;
Sunny Goyaldfaccf62015-05-11 16:30:44 -0700214 }
Samuel Fufa4a1d9b12019-09-19 17:28:00 -0700215
Winson1f064272016-07-18 17:18:02 -0700216 mCenterVertically = a.getBoolean(R.styleable.BubbleTextView_centerVertically, false);
Sunny Goyaldfaccf62015-05-11 16:30:44 -0700217
218 mIconSize = a.getDimensionPixelSize(R.styleable.BubbleTextView_iconSizeOverride,
219 defaultIconSize);
Adam Cohen96bb7982014-07-07 11:58:56 -0700220 a.recycle();
221
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800222 mLongPressHelper = new CheckLongPressHelper(this);
223
Tony Wickham8912b042018-11-29 15:28:53 -0800224 mDotParams = new DotRenderer.DrawParams();
225
Sunny Goyal66dccad2018-03-19 12:00:51 -0700226 setEllipsize(TruncateAt.END);
Sunny Goyal0b0847b2018-03-14 12:30:11 -0700227 setAccessibilityDelegate(mActivity.getAccessibilityDelegate());
Sunny Goyaled7a6932018-04-13 11:41:50 -0700228 setTextAlpha(1f);
Samuel Fufa314761a2020-10-14 10:15:07 -0700229
230 int shadowSize = context.getResources().getDimensionPixelSize(
231 R.dimen.blur_size_click_shadow);
232 mHighlightShadowFilter = new BlurMaskFilter(shadowSize, BlurMaskFilter.Blur.INNER);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800233 }
234
Sunny Goyal66dccad2018-03-19 12:00:51 -0700235 @Override
236 protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
237 // Disable marques when not focused to that, so that updating text does not cause relayout.
238 setEllipsize(focused ? TruncateAt.MARQUEE : TruncateAt.END);
239 super.onFocusChanged(focused, direction, previouslyFocusedRect);
240 }
241
Jon Miranda52549442017-10-26 11:28:06 -0700242 /**
243 * Resets the view so it can be recycled.
244 */
245 public void reset() {
Tony Wickhamf34bee82018-12-03 18:11:39 -0800246 mDotInfo = null;
Tony Wickham8912b042018-11-29 15:28:53 -0800247 mDotParams.color = Color.TRANSPARENT;
Tony Wickhamf34bee82018-12-03 18:11:39 -0800248 cancelDotScaleAnim();
Tony Wickham8912b042018-11-29 15:28:53 -0800249 mDotParams.scale = 0f;
Tony Wickhamf34bee82018-12-03 18:11:39 -0800250 mForceHideDot = false;
Samuel Fufaba6b47f2019-11-07 10:27:41 -0800251 setBackground(null);
Jon Miranda52549442017-10-26 11:28:06 -0700252 }
253
Tony Wickhamf34bee82018-12-03 18:11:39 -0800254 private void cancelDotScaleAnim() {
255 if (mDotScaleAnim != null) {
256 mDotScaleAnim.cancel();
Tony4043b052018-08-17 13:47:39 -0700257 }
258 }
259
Tony Wickhamf34bee82018-12-03 18:11:39 -0800260 private void animateDotScale(float... dotScales) {
261 cancelDotScaleAnim();
262 mDotScaleAnim = ObjectAnimator.ofFloat(this, DOT_SCALE_PROPERTY, dotScales);
263 mDotScaleAnim.addListener(new AnimatorListenerAdapter() {
Tony4043b052018-08-17 13:47:39 -0700264 @Override
265 public void onAnimationEnd(Animator animation) {
Tony Wickhamf34bee82018-12-03 18:11:39 -0800266 mDotScaleAnim = null;
Tony4043b052018-08-17 13:47:39 -0700267 }
268 });
Tony Wickhamf34bee82018-12-03 18:11:39 -0800269 mDotScaleAnim.start();
Tony4043b052018-08-17 13:47:39 -0700270 }
271
Hyunyoung Song5809a402021-02-02 01:19:18 -0800272 @UiThread
Sunny Goyal95899162019-03-27 16:03:06 -0700273 public void applyFromWorkspaceItem(WorkspaceItemInfo info) {
274 applyFromWorkspaceItem(info, false);
Winson Chung5f8afe62013-08-12 16:19:28 -0700275 }
276
Adam Cohen29794c52019-06-21 12:50:30 -0700277 @Override
278 public void setAccessibilityDelegate(AccessibilityDelegate delegate) {
279 if (delegate instanceof LauncherAccessibilityDelegate) {
280 super.setAccessibilityDelegate(delegate);
281 } else {
282 // NO-OP
283 // Workaround for b/129745295 where RecyclerView is setting our Accessibility
284 // delegate incorrectly. There are no cases when we shouldn't be using the
285 // LauncherAccessibilityDelegate for BubbleTextView.
286 }
287 }
288
Hyunyoung Song5809a402021-02-02 01:19:18 -0800289 @UiThread
Sunny Goyal95899162019-03-27 16:03:06 -0700290 public void applyFromWorkspaceItem(WorkspaceItemInfo info, boolean promiseStateChanged) {
Sunny Goyal179249d2017-12-19 16:49:24 -0800291 applyIconAndLabel(info);
Michael Jurka67b2f6c2010-11-17 12:33:46 -0800292 setTag(info);
Schneider Victor-tulias6a5f4312021-01-12 19:30:02 +0000293 applyLoadingState(promiseStateChanged);
Tony Wickhamf34bee82018-12-03 18:11:39 -0800294 applyDotState(info, false /* animate */);
Schneider Victor-tulias5f7f4cb2021-01-28 13:51:23 -0800295 setDownloadStateContentDescription(info, info.getProgressLevel());
Michael Jurka67b2f6c2010-11-17 12:33:46 -0800296 }
297
Hyunyoung Song5809a402021-02-02 01:19:18 -0800298 @UiThread
Sunny Goyal508da152014-08-14 10:53:27 -0700299 public void applyFromApplicationInfo(AppInfo info) {
Sunny Goyal179249d2017-12-19 16:49:24 -0800300 applyIconAndLabel(info);
Sunny Goyalf4204382016-07-13 10:46:07 -0700301
Sunny Goyal95899162019-03-27 16:03:06 -0700302 // We don't need to check the info since it's not a WorkspaceItemInfo
Winson Chung888b3a12015-03-13 11:14:16 -0700303 super.setTag(info);
Sunny Goyal34b65272015-03-11 16:56:52 -0700304
305 // Verify high res immediately
306 verifyHighRes();
Tony Wickham010d2552017-01-20 08:15:28 -0800307
Schneider Victor-tulias6a5f4312021-01-12 19:30:02 +0000308 if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) != 0) {
Schneider Victor-tuliasb40b98d2021-01-21 14:41:07 -0800309 applyProgressLevel();
Mario Bertschler08ffaae2017-03-20 11:30:27 -0700310 }
Tony Wickhamf34bee82018-12-03 18:11:39 -0800311 applyDotState(info, false /* animate */);
Schneider Victor-tulias5f7f4cb2021-01-28 13:51:23 -0800312 setDownloadStateContentDescription(info, info.getProgressLevel());
Sunny Goyal508da152014-08-14 10:53:27 -0700313 }
314
Samuel Fufad6bacdc2020-08-23 21:56:07 -0700315 /**
316 * Apply label and tag using a generic {@link ItemInfoWithIcon}
317 */
Hyunyoung Song5809a402021-02-02 01:19:18 -0800318 @UiThread
Samuel Fufad6bacdc2020-08-23 21:56:07 -0700319 public void applyFromItemInfoWithIcon(ItemInfoWithIcon info) {
Sunny Goyal179249d2017-12-19 16:49:24 -0800320 applyIconAndLabel(info);
Sunny Goyal95899162019-03-27 16:03:06 -0700321 // We don't need to check the info since it's not a WorkspaceItemInfo
Sunny Goyal0e08f162015-05-12 11:32:39 -0700322 super.setTag(info);
323
324 // Verify high res immediately
325 verifyHighRes();
Schneider Victor-tulias5f7f4cb2021-01-28 13:51:23 -0800326
327 setDownloadStateContentDescription(info, info.getProgressLevel());
Sunny Goyal0e08f162015-05-12 11:32:39 -0700328 }
329
Samuel Fufa02be2a42020-09-23 00:45:27 -0700330 /**
Samuel Fufac2820182021-01-22 11:47:25 -0600331 * Apply label and tag using a {@link SearchActionItemInfo}
Samuel Fufa02be2a42020-09-23 00:45:27 -0700332 */
Hyunyoung Song5809a402021-02-02 01:19:18 -0800333 @UiThread
Samuel Fufac2820182021-01-22 11:47:25 -0600334 public void applyFromSearchActionItemInfo(SearchActionItemInfo searchActionItemInfo) {
335 applyIconAndLabel(searchActionItemInfo);
336 setTag(searchActionItemInfo);
Samuel Fufa02be2a42020-09-23 00:45:27 -0700337 }
338
Hyunyoung Song5809a402021-02-02 01:19:18 -0800339 @UiThread
Samuel Fufac2820182021-01-22 11:47:25 -0600340 protected void applyIconAndLabel(ItemInfoWithIcon info) {
Sunny Goyal14168432019-10-24 15:59:49 -0700341 FastBitmapDrawable iconDrawable = newIcon(getContext(), info);
Sunny Goyal3808a692019-10-25 13:41:28 -0700342 mDotParams.color = IconPalette.getMutedColor(info.bitmap.color, 0.54f);
Sunny Goyal179249d2017-12-19 16:49:24 -0800343
Sunny Goyalf4204382016-07-13 10:46:07 -0700344 setIcon(iconDrawable);
Schneider Victor-tulias6a5f4312021-01-12 19:30:02 +0000345 applyLabel(info);
346 }
347
Hyunyoung Song5809a402021-02-02 01:19:18 -0800348 @UiThread
Schneider Victor-tulias6a5f4312021-01-12 19:30:02 +0000349 private void applyLabel(ItemInfoWithIcon info) {
Sunny Goyalf4204382016-07-13 10:46:07 -0700350 setText(info.title);
351 if (info.contentDescription != null) {
352 setContentDescription(info.isDisabled()
353 ? getContext().getString(R.string.disabled_app_label, info.contentDescription)
354 : info.contentDescription);
355 }
356 }
357
Winson Chung7501adf2015-06-02 11:24:28 -0700358 /**
Tony Wickhamaaa42bd2021-01-27 16:27:15 -0800359 * Directly set the icon and label.
360 */
361 @UiThread
362 public void applyIconAndLabel(Drawable icon, CharSequence label) {
363 setIcon(icon);
364 setText(label);
365 setContentDescription(label);
366 }
367
368 /**
Winson Chung7501adf2015-06-02 11:24:28 -0700369 * Overrides the default long press timeout.
370 */
Tony2ca999c2018-09-24 17:24:51 -0400371 public void setLongPressTimeoutFactor(float longPressTimeoutFactor) {
372 mLongPressHelper.setLongPressTimeoutFactor(longPressTimeoutFactor);
Winson Chung7501adf2015-06-02 11:24:28 -0700373 }
Sunny Goyal0e08f162015-05-12 11:32:39 -0700374
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800375 @Override
Sunny Goyal2a76e3f2017-02-16 13:33:15 -0800376 public void refreshDrawableState() {
Sunny Goyal508da152014-08-14 10:53:27 -0700377 if (!mIgnorePressedStateChange) {
Sunny Goyal2a76e3f2017-02-16 13:33:15 -0800378 super.refreshDrawableState();
Michael Jurka38b4f7c2010-12-14 16:46:39 -0800379 }
Sunny Goyal508da152014-08-14 10:53:27 -0700380 }
Michael Jurka38b4f7c2010-12-14 16:46:39 -0800381
Sunny Goyal2a76e3f2017-02-16 13:33:15 -0800382 @Override
383 protected int[] onCreateDrawableState(int extraSpace) {
384 final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
385 if (mStayPressed) {
386 mergeDrawableStates(drawableState, STATE_PRESSED);
387 }
388 return drawableState;
389 }
390
Winson Chungb745afb2015-03-02 11:51:23 -0800391 /** Returns the icon for this view. */
392 public Drawable getIcon() {
393 return mIcon;
394 }
395
Tony Wickham1bce7fd2016-04-28 17:39:03 -0700396 @Override
Michael Jurka38b4f7c2010-12-14 16:46:39 -0800397 public boolean onTouchEvent(MotionEvent event) {
Samuel Fufa4a1d9b12019-09-19 17:28:00 -0700398 // ignore events if they happen in padding area
Sunny Goyal17feee82020-03-24 13:55:15 -0700399 if (event.getAction() == MotionEvent.ACTION_DOWN
Sunny Goyal44a3b202020-07-09 08:27:06 -0700400 && shouldIgnoreTouchDown(event.getX(), event.getY())) {
Samuel Fufa4a1d9b12019-09-19 17:28:00 -0700401 return false;
402 }
Sunny Goyal17feee82020-03-24 13:55:15 -0700403 if (isLongClickable()) {
404 super.onTouchEvent(event);
405 mLongPressHelper.onTouchEvent(event);
406 // Keep receiving the rest of the events
407 return true;
408 } else {
409 return super.onTouchEvent(event);
Mady Melloref044dd2015-06-02 15:35:07 -0700410 }
Michael Jurka38b4f7c2010-12-14 16:46:39 -0800411 }
412
Sunny Goyal44a3b202020-07-09 08:27:06 -0700413 /**
414 * Returns true if the touch down at the provided position be ignored
415 */
416 protected boolean shouldIgnoreTouchDown(float x, float y) {
417 return y < getPaddingTop()
418 || x < getPaddingLeft()
419 || y > getHeight() - getPaddingBottom()
420 || x > getWidth() - getPaddingRight();
421 }
422
Michael Jurkaddd62e92011-02-16 17:49:14 -0800423 void setStayPressed(boolean stayPressed) {
424 mStayPressed = stayPressed;
Sunny Goyal2a76e3f2017-02-16 13:33:15 -0800425 refreshDrawableState();
Patrick Dubroy3499d8c2011-03-10 17:17:23 -0800426 }
427
Sunny Goyal3dce5f32017-10-05 11:40:05 -0700428 @Override
Hyunyoung Songef468d82019-01-03 01:02:43 -0800429 public void onVisibilityAggregated(boolean isVisible) {
430 super.onVisibilityAggregated(isVisible);
431 if (mIcon != null) {
Hyunyoung Songa2373852019-01-04 12:01:32 -0800432 mIcon.setVisible(isVisible, false);
Hyunyoung Songef468d82019-01-03 01:02:43 -0800433 }
434 }
435
436 @Override
Sunny Goyal3dce5f32017-10-05 11:40:05 -0700437 public void onLauncherResume() {
438 // Reset the pressed state of icon that was locked in the press state while activity
439 // was launching
440 setStayPressed(false);
441 }
442
Sunny Goyal508da152014-08-14 10:53:27 -0700443 void clearPressedBackground() {
444 setPressed(false);
445 setStayPressed(false);
446 }
447
448 @Override
Sunny Goyal508da152014-08-14 10:53:27 -0700449 public boolean onKeyUp(int keyCode, KeyEvent event) {
450 // Unlike touch events, keypress event propagate pressed state change immediately,
451 // without waiting for onClickHandler to execute. Disable pressed state changes here
452 // to avoid flickering.
453 mIgnorePressedStateChange = true;
454 boolean result = super.onKeyUp(keyCode, event);
Sunny Goyal508da152014-08-14 10:53:27 -0700455 mIgnorePressedStateChange = false;
Sunny Goyal2a76e3f2017-02-16 13:33:15 -0800456 refreshDrawableState();
Sunny Goyal508da152014-08-14 10:53:27 -0700457 return result;
Michael Jurkaddd62e92011-02-16 17:49:14 -0800458 }
Patrick Dubroya017c032011-03-09 15:58:32 -0800459
Sunny Goyal9314b7c2017-06-23 10:36:27 -0700460 @SuppressWarnings("wrongcall")
Tony Wickhamf34bee82018-12-03 18:11:39 -0800461 protected void drawWithoutDot(Canvas canvas) {
Sunny Goyal9314b7c2017-06-23 10:36:27 -0700462 super.onDraw(canvas);
463 }
464
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800465 @Override
Sunny Goyal9314b7c2017-06-23 10:36:27 -0700466 public void onDraw(Canvas canvas) {
Samuel Fufa314761a2020-10-14 10:15:07 -0700467 if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && mHighlightColor != Color.TRANSPARENT) {
468 int count = canvas.save();
469 drawFocusHighlight(canvas);
470 canvas.restoreToCount(count);
471 }
Sunny Goyal9314b7c2017-06-23 10:36:27 -0700472 super.onDraw(canvas);
Tony Wickham22a5a682020-11-02 17:04:58 -0800473 drawDotIfNecessary(canvas);
Samuel Fufa314761a2020-10-14 10:15:07 -0700474 }
475
476 protected void drawFocusHighlight(Canvas canvas) {
477 boolean isBadged = getTag() instanceof ItemInfo && !Process.myUserHandle().equals(
478 ((ItemInfo) getTag()).user);
479 float insetScale = (HIGHLIGHT_SCALE - 1) / 2;
480 canvas.translate(-getIconSize() * insetScale, -insetScale * getIconSize());
481 float outlineSize = getIconSize() * HIGHLIGHT_SCALE;
482 mHighlightPath.reset();
483 mHighlightPaint.reset();
484 getIconBounds(mDotParams.iconBounds);
485 getShape().addToPath(mHighlightPath, mDotParams.iconBounds.left, mDotParams.iconBounds.top,
486 outlineSize / 2);
487 if (isBadged) {
488 float borderSize = outlineSize - getIconSize();
489 float badgeSize = LauncherIcons.getBadgeSizeForIconSize(getIconSize()) + borderSize;
490 float badgeInset = outlineSize - badgeSize;
491 getShape().addToPath(mHighlightPath, mDotParams.iconBounds.left + badgeInset,
492 mDotParams.iconBounds.top + badgeInset, badgeSize / 2);
493 }
494 mHighlightPaint.setMaskFilter(mHighlightShadowFilter);
495 mHighlightPaint.setColor(mDotParams.color);
496 canvas.drawPath(mHighlightPath, mHighlightPaint);
497 mHighlightPaint.setMaskFilter(null);
498 mHighlightPaint.setColor(mHighlightColor);
499 canvas.drawPath(mHighlightPath, mHighlightPaint);
Tony Wickham1237df02017-02-24 08:59:36 -0800500 }
501
502 /**
Tony Wickhamf34bee82018-12-03 18:11:39 -0800503 * Draws the notification dot in the top right corner of the icon bounds.
Samuel Fufad6bacdc2020-08-23 21:56:07 -0700504 *
Tony Wickham1237df02017-02-24 08:59:36 -0800505 * @param canvas The canvas to draw to.
506 */
Tony Wickhamf34bee82018-12-03 18:11:39 -0800507 protected void drawDotIfNecessary(Canvas canvas) {
Tony Wickham7ba547c2021-02-02 17:12:08 -0800508 if (mActivity instanceof Launcher && ((Launcher) mActivity).isViewInTaskbar(this)) {
Tony Wickham794fe4f2021-01-11 14:54:23 -0600509 // TODO: support notification dots in Taskbar
510 return;
511 }
Tony Wickhamf34bee82018-12-03 18:11:39 -0800512 if (!mForceHideDot && (hasDot() || mDotParams.scale > 0)) {
Tony Wickham8912b042018-11-29 15:28:53 -0800513 getIconBounds(mDotParams.iconBounds);
Samuel Fufad6bacdc2020-08-23 21:56:07 -0700514 Utilities.scaleRectAboutCenter(mDotParams.iconBounds,
515 IconShape.getNormalizationScale());
Tony Wickham1237df02017-02-24 08:59:36 -0800516 final int scrollX = getScrollX();
517 final int scrollY = getScrollY();
518 canvas.translate(scrollX, scrollY);
Tony Wickham8912b042018-11-29 15:28:53 -0800519 mDotRenderer.draw(canvas, mDotParams);
Tony Wickham1237df02017-02-24 08:59:36 -0800520 canvas.translate(-scrollX, -scrollY);
521 }
522 }
523
Jon Miranda9b78e192019-08-30 18:42:01 -0700524 @Override
525 public void setForceHideDot(boolean forceHideDot) {
Tony Wickhamf34bee82018-12-03 18:11:39 -0800526 if (mForceHideDot == forceHideDot) {
Tony Wickham1237df02017-02-24 08:59:36 -0800527 return;
528 }
Tony Wickhamf34bee82018-12-03 18:11:39 -0800529 mForceHideDot = forceHideDot;
Tony Wickham1237df02017-02-24 08:59:36 -0800530
Tony Wickhamf34bee82018-12-03 18:11:39 -0800531 if (forceHideDot) {
Tony Wickham1237df02017-02-24 08:59:36 -0800532 invalidate();
Tony Wickhamf34bee82018-12-03 18:11:39 -0800533 } else if (hasDot()) {
534 animateDotScale(0, 1);
Tony Wickham1237df02017-02-24 08:59:36 -0800535 }
536 }
537
Tony Wickhamf34bee82018-12-03 18:11:39 -0800538 private boolean hasDot() {
539 return mDotInfo != null;
Tony Wickham1237df02017-02-24 08:59:36 -0800540 }
541
542 public void getIconBounds(Rect outBounds) {
Tony05d98c22018-07-23 08:01:15 -0700543 getIconBounds(this, outBounds, mIconSize);
544 }
545
546 public static void getIconBounds(View iconView, Rect outBounds, int iconSize) {
547 int top = iconView.getPaddingTop();
548 int left = (iconView.getWidth() - iconSize) / 2;
549 int right = left + iconSize;
550 int bottom = top + iconSize;
Tony Wickham1237df02017-02-24 08:59:36 -0800551 outBounds.set(left, top, right, bottom);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800552 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400553
Jon Miranda228877d2021-02-09 11:05:00 -0500554
555 /**
556 * Sets whether to vertically center the content.
557 */
558 public void setCenterVertically(boolean centerVertically) {
559 mCenterVertically = centerVertically;
560 }
561
Joe Onorato9c1289c2009-08-17 11:03:03 -0400562 @Override
Winson1f064272016-07-18 17:18:02 -0700563 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
564 if (mCenterVertically) {
565 Paint.FontMetrics fm = getPaint().getFontMetrics();
566 int cellHeightPx = mIconSize + getCompoundDrawablePadding() +
567 (int) Math.ceil(fm.bottom - fm.top);
568 int height = MeasureSpec.getSize(heightMeasureSpec);
569 setPadding(getPaddingLeft(), (height - cellHeightPx) / 2, getPaddingRight(),
570 getPaddingBottom());
571 }
572 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
573 }
574
575 @Override
Adam Cohen477828c2013-09-20 12:05:49 -0700576 public void setTextColor(int color) {
577 mTextColor = color;
Sunny Goyaled7a6932018-04-13 11:41:50 -0700578 super.setTextColor(getModifiedColor());
Adam Cohen477828c2013-09-20 12:05:49 -0700579 }
580
Adam Cohen96bb7982014-07-07 11:58:56 -0700581 @Override
582 public void setTextColor(ColorStateList colors) {
583 mTextColor = colors.getDefaultColor();
Sunny Goyaled7a6932018-04-13 11:41:50 -0700584 if (Float.compare(mTextAlpha, 1) == 0) {
585 super.setTextColor(colors);
586 } else {
587 super.setTextColor(getModifiedColor());
588 }
Adam Cohen477828c2013-09-20 12:05:49 -0700589 }
590
Tony7308cde2017-06-27 22:38:33 -0700591 public boolean shouldTextBeVisible() {
592 // Text should be visible everywhere but the hotseat.
593 Object tag = getParent() instanceof FolderIcon ? ((View) getParent()).getTag() : getTag();
594 ItemInfo info = tag instanceof ItemInfo ? (ItemInfo) tag : null;
Samuel Fufaefb665d2019-10-30 13:24:14 -0700595 return info == null || (info.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT
596 && info.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION);
Tony7308cde2017-06-27 22:38:33 -0700597 }
598
Winson Chung5f8afe62013-08-12 16:19:28 -0700599 public void setTextVisibility(boolean visible) {
Sunny Goyaled7a6932018-04-13 11:41:50 -0700600 setTextAlpha(visible ? 1 : 0);
601 }
602
603 private void setTextAlpha(float alpha) {
604 mTextAlpha = alpha;
605 super.setTextColor(getModifiedColor());
606 }
607
608 private int getModifiedColor() {
609 if (mTextAlpha == 0) {
610 // Special case to prevent text shadows in high contrast mode
611 return Color.TRANSPARENT;
Winson Chung5f8afe62013-08-12 16:19:28 -0700612 }
Sunny Goyal066ace12018-11-05 11:08:31 -0800613 return setColorAlphaBound(mTextColor, Math.round(Color.alpha(mTextColor) * mTextAlpha));
Tony8f402802017-06-16 17:24:54 -0700614 }
615
616 /**
617 * Creates an animator to fade the text in or out.
Samuel Fufad6bacdc2020-08-23 21:56:07 -0700618 *
Tony8f402802017-06-16 17:24:54 -0700619 * @param fadeIn Whether the text should fade in or fade out.
620 */
Sunny Goyal4e5a8782017-06-23 09:34:06 -0700621 public ObjectAnimator createTextAlphaAnimator(boolean fadeIn) {
Sunny Goyaled7a6932018-04-13 11:41:50 -0700622 float toAlpha = shouldTextBeVisible() && fadeIn ? 1 : 0;
Jon Miranda83cdca62019-04-15 13:01:05 -0700623 return ObjectAnimator.ofFloat(this, TEXT_ALPHA_PROPERTY, toAlpha);
Tony8f402802017-06-16 17:24:54 -0700624 }
625
Winson Chungaffd7b42010-08-20 15:11:56 -0700626 @Override
Winson Chung88f33452012-02-23 15:23:44 -0800627 public void cancelLongPress() {
628 super.cancelLongPress();
Winson Chung88f33452012-02-23 15:23:44 -0800629 mLongPressHelper.cancelLongPress();
630 }
Chris Wrenaeff7ea2014-02-14 16:59:24 -0500631
Samuel Fufac2820182021-01-22 11:47:25 -0600632 /**
633 * Applies the loading progress value to the progress bar.
Schneider Victor-tulias6a5f4312021-01-12 19:30:02 +0000634 *
635 * If this app is installing, the progress bar will be updated with the installation progress.
636 * If this app is installed and downloading incrementally, the progress bar will be updated
637 * with the total download progress.
638 */
639 public void applyLoadingState(boolean promiseStateChanged) {
Schneider Victor-tuliasb40b98d2021-01-21 14:41:07 -0800640 if (getTag() instanceof ItemInfoWithIcon) {
Sunny Goyal95899162019-03-27 16:03:06 -0700641 WorkspaceItemInfo info = (WorkspaceItemInfo) getTag();
Schneider Victor-tulias6a5f4312021-01-12 19:30:02 +0000642 if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_INCREMENTAL_DOWNLOAD_ACTIVE)
643 != 0) {
Schneider Victor-tuliasb40b98d2021-01-21 14:41:07 -0800644 updateProgressBarUi(info.getProgressLevel() == 100);
Schneider Victor-tulias6a5f4312021-01-12 19:30:02 +0000645 } else if (info.hasPromiseIconUi() || (info.runtimeStatusFlags
Schneider Victor-tuliasb40b98d2021-01-21 14:41:07 -0800646 & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
647 updateProgressBarUi(promiseStateChanged);
Mario Bertschler08ffaae2017-03-20 11:30:27 -0700648 }
649 }
650 }
651
Schneider Victor-tuliasb40b98d2021-01-21 14:41:07 -0800652 private void updateProgressBarUi(boolean maybePerformFinishedAnimation) {
653 PreloadIconDrawable preloadDrawable = applyProgressLevel();
Schneider Victor-tulias6a5f4312021-01-12 19:30:02 +0000654 if (preloadDrawable != null && maybePerformFinishedAnimation) {
655 preloadDrawable.maybePerformFinishedAnimation();
656 }
657 }
658
659 /** Applies the given progress level to the this icon's progress bar. */
660 @Nullable
Schneider Victor-tuliasb40b98d2021-01-21 14:41:07 -0800661 public PreloadIconDrawable applyProgressLevel() {
662 if (!(getTag() instanceof ItemInfoWithIcon)) {
663 return null;
664 }
665
666 ItemInfoWithIcon info = (ItemInfoWithIcon) getTag();
667 int progressLevel = info.getProgressLevel();
668 if (progressLevel >= 100) {
669 setContentDescription(info.contentDescription != null
670 ? info.contentDescription : "");
671 } else if (progressLevel > 0) {
Schneider Victor-tulias5f7f4cb2021-01-28 13:51:23 -0800672 setDownloadStateContentDescription(info, progressLevel);
Schneider Victor-tuliasb40b98d2021-01-21 14:41:07 -0800673 } else {
674 setContentDescription(getContext()
675 .getString(R.string.app_waiting_download_title, info.title));
676 }
677 if (mIcon != null) {
678 PreloadIconDrawable preloadIconDrawable;
679 if (mIcon instanceof PreloadIconDrawable) {
680 preloadIconDrawable = (PreloadIconDrawable) mIcon;
681 preloadIconDrawable.setLevel(progressLevel);
682 preloadIconDrawable.setIsDisabled(!info.isAppStartable());
Mario Bertschler230612f2017-09-27 17:05:21 -0700683 } else {
Schneider Victor-tuliasb40b98d2021-01-21 14:41:07 -0800684 preloadIconDrawable = makePreloadIcon();
685 setIcon(preloadIconDrawable);
Mario Bertschler230612f2017-09-27 17:05:21 -0700686 }
Schneider Victor-tuliasb40b98d2021-01-21 14:41:07 -0800687 return preloadIconDrawable;
Chris Wren40c5ed32014-06-24 18:24:23 -0400688 }
Mario Bertschler08ffaae2017-03-20 11:30:27 -0700689 return null;
Chris Wren40c5ed32014-06-24 18:24:23 -0400690 }
Sunny Goyal95abbb32014-08-04 10:53:22 -0700691
Schneider Victor-tuliasb40b98d2021-01-21 14:41:07 -0800692 /**
693 * Creates a PreloadIconDrawable with the appropriate progress level without mutating this
694 * object.
695 */
696 @Nullable
697 public PreloadIconDrawable makePreloadIcon() {
698 if (!(getTag() instanceof ItemInfoWithIcon)) {
699 return null;
700 }
701
702 ItemInfoWithIcon info = (ItemInfoWithIcon) getTag();
703 int progressLevel = info.getProgressLevel();
704 final PreloadIconDrawable preloadDrawable = newPendingIcon(getContext(), info);
705
706 preloadDrawable.setLevel(progressLevel);
707 preloadDrawable.setIsDisabled(!info.isAppStartable());
708
709 return preloadDrawable;
710 }
711
Tony Wickhamf34bee82018-12-03 18:11:39 -0800712 public void applyDotState(ItemInfo itemInfo, boolean animate) {
Tony Wickham9a8d11f2017-01-11 09:53:12 -0800713 if (mIcon instanceof FastBitmapDrawable) {
Tony Wickhamf34bee82018-12-03 18:11:39 -0800714 boolean wasDotted = mDotInfo != null;
715 mDotInfo = mActivity.getDotInfoForItem(itemInfo);
716 boolean isDotted = mDotInfo != null;
717 float newDotScale = isDotted ? 1f : 0;
Samuel Fufac96fa242019-09-26 23:06:32 -0700718 if (mDisplay == DISPLAY_ALL_APPS) {
719 mDotRenderer = mActivity.getDeviceProfile().mDotRendererAllApps;
720 } else {
721 mDotRenderer = mActivity.getDeviceProfile().mDotRendererWorkSpace;
722 }
Tony Wickhamf34bee82018-12-03 18:11:39 -0800723 if (wasDotted || isDotted) {
724 // Animate when a dot is first added or when it is removed.
725 if (animate && (wasDotted ^ isDotted) && isShown()) {
726 animateDotScale(newDotScale);
Tony Wickham1237df02017-02-24 08:59:36 -0800727 } else {
Tony Wickhamf34bee82018-12-03 18:11:39 -0800728 cancelDotScaleAnim();
729 mDotParams.scale = newDotScale;
Tony Wickham1237df02017-02-24 08:59:36 -0800730 invalidate();
731 }
732 }
Tony Wickham305e9202018-01-23 17:46:47 -0800733 if (itemInfo.contentDescription != null) {
vadimtda6dad52019-05-21 13:24:23 -0700734 if (itemInfo.isDisabled()) {
735 setContentDescription(getContext().getString(R.string.disabled_app_label,
736 itemInfo.contentDescription));
737 } else if (hasDot()) {
Tony Wickhamf34bee82018-12-03 18:11:39 -0800738 int count = mDotInfo.getNotificationCount();
Tony Wickham305e9202018-01-23 17:46:47 -0800739 setContentDescription(getContext().getResources().getQuantityString(
Tony Wickhamf34bee82018-12-03 18:11:39 -0800740 R.plurals.dotted_app_label, count, itemInfo.contentDescription, count));
Tony Wickham305e9202018-01-23 17:46:47 -0800741 } else {
742 setContentDescription(itemInfo.contentDescription);
743 }
744 }
Tony Wickham9a8d11f2017-01-11 09:53:12 -0800745 }
746 }
747
Schneider Victor-tulias5f7f4cb2021-01-28 13:51:23 -0800748 private void setDownloadStateContentDescription(ItemInfoWithIcon info, int progressLevel) {
749 if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK)
750 != 0) {
751 String percentageString = NumberFormat.getPercentInstance()
752 .format(progressLevel * 0.01);
753 if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
754 setContentDescription(getContext()
755 .getString(
756 R.string.app_installing_title, info.title, percentageString));
757 } else if ((info.runtimeStatusFlags
758 & ItemInfoWithIcon.FLAG_INCREMENTAL_DOWNLOAD_ACTIVE) != 0) {
759 setContentDescription(getContext()
760 .getString(
761 R.string.app_downloading_title, info.title, percentageString));
762 }
763 }
764 }
765
Winson Chungb745afb2015-03-02 11:51:23 -0800766 /**
767 * Sets the icon for this view based on the layout direction.
768 */
Samuel Fufa4b7f38b2020-10-06 18:37:46 -0700769 protected void setIcon(Drawable icon) {
Tony17b7f9b2017-05-23 12:19:09 -0700770 if (mIsIconVisible) {
Sunny Goyal66dccad2018-03-19 12:00:51 -0700771 applyCompoundDrawables(icon);
Tony17b7f9b2017-05-23 12:19:09 -0700772 }
Sunny Goyal66dccad2018-03-19 12:00:51 -0700773 mIcon = icon;
Hyunyoung Songa2373852019-01-04 12:01:32 -0800774 if (mIcon != null) {
775 mIcon.setVisible(getWindowVisibility() == VISIBLE && isShown(), false);
776 }
Tony17b7f9b2017-05-23 12:19:09 -0700777 }
778
Jon Miranda9b78e192019-08-30 18:42:01 -0700779 @Override
Tony17b7f9b2017-05-23 12:19:09 -0700780 public void setIconVisible(boolean visible) {
781 mIsIconVisible = visible;
Sunny Goyal30ac97d2020-06-24 23:47:46 -0700782 if (!mIsIconVisible) {
783 resetIconScale();
784 }
Sunny Goyal66dccad2018-03-19 12:00:51 -0700785 Drawable icon = visible ? mIcon : new ColorDrawable(Color.TRANSPARENT);
Tony17b7f9b2017-05-23 12:19:09 -0700786 applyCompoundDrawables(icon);
Tony Wickham377ed3f2016-07-20 15:21:04 -0700787 }
788
Schneider Victor-tulias7bbec502020-10-29 11:39:24 -0400789 protected boolean iconUpdateAnimationEnabled() {
790 return mEnableIconUpdateAnimation;
791 }
792
Tony Wickham377ed3f2016-07-20 15:21:04 -0700793 protected void applyCompoundDrawables(Drawable icon) {
Sunny Goyal66dccad2018-03-19 12:00:51 -0700794 // If we had already set an icon before, disable relayout as the icon size is the
795 // same as before.
796 mDisableRelayout = mIcon != null;
797
798 icon.setBounds(0, 0, mIconSize, mIconSize);
Schneider Victor-tulias350b4622020-09-18 11:16:30 -0700799
800 updateIcon(icon);
801
802 // If the current icon is a placeholder color, animate its update.
Schneider Victor-tulias3ee70182020-10-21 14:01:53 -0400803 if (mIcon != null
804 && mIcon instanceof PlaceHolderIconDrawable
Schneider Victor-tulias7bbec502020-10-29 11:39:24 -0400805 && iconUpdateAnimationEnabled()) {
Steven Nge92bc552021-02-10 17:10:15 +0000806 ((PlaceHolderIconDrawable) mIcon).animateIconUpdate(icon);
Winson Chungb745afb2015-03-02 11:51:23 -0800807 }
Schneider Victor-tulias350b4622020-09-18 11:16:30 -0700808
Sunny Goyal66dccad2018-03-19 12:00:51 -0700809 mDisableRelayout = false;
Winson Chungb745afb2015-03-02 11:51:23 -0800810 }
Sunny Goyal34b65272015-03-11 16:56:52 -0700811
Sunny Goyal69b75642015-05-15 17:00:24 -0700812 @Override
813 public void requestLayout() {
814 if (!mDisableRelayout) {
815 super.requestLayout();
816 }
817 }
818
Sunny Goyal34b65272015-03-11 16:56:52 -0700819 /**
820 * Applies the item info if it is same as what the view is pointing to currently.
821 */
Sunny Goyal2d7cca12017-01-03 16:52:43 -0800822 @Override
823 public void reapplyItemInfo(ItemInfoWithIcon info) {
Sunny Goyal34b65272015-03-11 16:56:52 -0700824 if (getTag() == info) {
825 mIconLoadRequest = null;
Sunny Goyal69b75642015-05-15 17:00:24 -0700826 mDisableRelayout = true;
Schneider Victor-tulias7bbec502020-10-29 11:39:24 -0400827 mEnableIconUpdateAnimation = true;
Winsonc0880492015-08-21 11:16:27 -0700828
Jon Miranda7f522a22017-07-28 11:56:47 -0700829 // Optimization: Starting in N, pre-uploads the bitmap to RenderThread.
Sunny Goyal3808a692019-10-25 13:41:28 -0700830 info.bitmap.icon.prepareToDraw();
Jon Miranda7f522a22017-07-28 11:56:47 -0700831
Sunny Goyal34b65272015-03-11 16:56:52 -0700832 if (info instanceof AppInfo) {
833 applyFromApplicationInfo((AppInfo) info);
Sunny Goyal95899162019-03-27 16:03:06 -0700834 } else if (info instanceof WorkspaceItemInfo) {
835 applyFromWorkspaceItem((WorkspaceItemInfo) info);
Sunny Goyal0b0847b2018-03-14 12:30:11 -0700836 mActivity.invalidateParent(info);
Sunny Goyal0e08f162015-05-12 11:32:39 -0700837 } else if (info instanceof PackageItemInfo) {
Samuel Fufad6bacdc2020-08-23 21:56:07 -0700838 applyFromItemInfoWithIcon((PackageItemInfo) info);
Samuel Fufac2820182021-01-22 11:47:25 -0600839 } else if (info instanceof SearchActionItemInfo) {
840 applyFromSearchActionItemInfo((SearchActionItemInfo) info);
Sunny Goyal34b65272015-03-11 16:56:52 -0700841 }
Winsonc0880492015-08-21 11:16:27 -0700842
Sunny Goyal69b75642015-05-15 17:00:24 -0700843 mDisableRelayout = false;
Schneider Victor-tulias7bbec502020-10-29 11:39:24 -0400844 mEnableIconUpdateAnimation = false;
Sunny Goyal34b65272015-03-11 16:56:52 -0700845 }
846 }
847
848 /**
849 * Verifies that the current icon is high-res otherwise posts a request to load the icon.
850 */
851 public void verifyHighRes() {
852 if (mIconLoadRequest != null) {
853 mIconLoadRequest.cancel();
854 mIconLoadRequest = null;
855 }
Sunny Goyal2d7cca12017-01-03 16:52:43 -0800856 if (getTag() instanceof ItemInfoWithIcon) {
857 ItemInfoWithIcon info = (ItemInfoWithIcon) getTag();
Sunny Goyal2b787e52018-08-20 15:01:03 -0700858 if (info.usingLowResIcon()) {
Sunny Goyal87f784c2017-01-11 10:48:34 -0800859 mIconLoadRequest = LauncherAppState.getInstance(getContext()).getIconCache()
Sunny Goyal0e08f162015-05-12 11:32:39 -0700860 .updateIconInBackground(BubbleTextView.this, info);
861 }
Sunny Goyal34b65272015-03-11 16:56:52 -0700862 }
863 }
Sunny Goyal4b6eb262015-05-14 19:24:40 -0700864
Jon Miranda47170112017-03-03 14:57:14 -0800865 public int getIconSize() {
866 return mIconSize;
867 }
Adam Cohen65086992020-02-19 08:40:49 -0800868
Adam Cohen1d13c0b2020-04-21 16:29:12 -0700869 private void updateTranslation() {
870 super.setTranslationX(mTranslationForReorderBounce.x + mTranslationForReorderPreview.x);
871 super.setTranslationY(mTranslationForReorderBounce.y + mTranslationForReorderPreview.y);
Adam Cohend9162062020-03-24 16:35:35 -0700872 }
873
Adam Cohen1d13c0b2020-04-21 16:29:12 -0700874 public void setReorderBounceOffset(float x, float y) {
875 mTranslationForReorderBounce.set(x, y);
876 updateTranslation();
Adam Cohend9162062020-03-24 16:35:35 -0700877 }
878
Adam Cohen1d13c0b2020-04-21 16:29:12 -0700879 public void getReorderBounceOffset(PointF offset) {
880 offset.set(mTranslationForReorderBounce);
881 }
882
883 @Override
884 public void setReorderPreviewOffset(float x, float y) {
885 mTranslationForReorderPreview.set(x, y);
886 updateTranslation();
887 }
888
889 @Override
890 public void getReorderPreviewOffset(PointF offset) {
891 offset.set(mTranslationForReorderPreview);
892 }
893
894 public void setReorderBounceScale(float scale) {
895 mScaleForReorderBounce = scale;
Adam Cohend9162062020-03-24 16:35:35 -0700896 super.setScaleX(scale);
897 super.setScaleY(scale);
898 }
899
Adam Cohen1d13c0b2020-04-21 16:29:12 -0700900 public float getReorderBounceScale() {
901 return mScaleForReorderBounce;
Adam Cohend9162062020-03-24 16:35:35 -0700902 }
903
904 public View getView() {
905 return this;
906 }
907
Adam Cohen65086992020-02-19 08:40:49 -0800908 @Override
909 public int getViewType() {
910 return DRAGGABLE_ICON;
911 }
912
913 @Override
Adam Cohenc77bc452020-05-07 11:55:19 -0700914 public void getWorkspaceVisualDragBounds(Rect bounds) {
Adam Cohen65086992020-02-19 08:40:49 -0800915 DeviceProfile grid = mActivity.getDeviceProfile();
916 BubbleTextView.getIconBounds(this, bounds, grid.iconSizePx);
917 }
918
Adam Cohenc77bc452020-05-07 11:55:19 -0700919 private int getIconSizeForDisplay(int display) {
920 DeviceProfile grid = mActivity.getDeviceProfile();
921 switch (display) {
922 case DISPLAY_ALL_APPS:
923 return grid.allAppsIconSizePx;
924 case DISPLAY_WORKSPACE:
925 case DISPLAY_FOLDER:
926 default:
927 return grid.iconSizePx;
928 }
929 }
930
931 public void getSourceVisualDragBounds(Rect bounds) {
932 BubbleTextView.getIconBounds(this, bounds, getIconSizeForDisplay(mDisplay));
933 }
934
Adam Cohen65086992020-02-19 08:40:49 -0800935 @Override
Samuel Fufa167210a2020-05-12 18:40:11 -0700936 public SafeCloseable prepareDrawDragView() {
Samuel Fufa314761a2020-10-14 10:15:07 -0700937 int highlightColor = mHighlightColor;
938 mHighlightColor = Color.TRANSPARENT;
Sunny Goyal30ac97d2020-06-24 23:47:46 -0700939 resetIconScale();
Adam Cohen65086992020-02-19 08:40:49 -0800940 setForceHideDot(true);
Samuel Fufa314761a2020-10-14 10:15:07 -0700941 return () -> mHighlightColor = highlightColor;
Adam Cohen65086992020-02-19 08:40:49 -0800942 }
Sunny Goyal30ac97d2020-06-24 23:47:46 -0700943
944 private void resetIconScale() {
945 if (mIcon instanceof FastBitmapDrawable) {
946 ((FastBitmapDrawable) mIcon).setScale(1f);
947 }
948 }
Schneider Victor-tulias350b4622020-09-18 11:16:30 -0700949
950 private void updateIcon(Drawable newIcon) {
951 if (mLayoutHorizontal) {
952 setCompoundDrawablesRelative(newIcon, null, null, null);
953 } else {
954 setCompoundDrawables(null, newIcon, null, null);
955 }
956 }
957
Samuel Fufa314761a2020-10-14 10:15:07 -0700958 @Override
959 public void decorate(int color) {
960 mHighlightColor = color;
961 invalidate();
962 }
963
964 @Override
965 public void removeDecoration() {
966 mHighlightColor = Color.TRANSPARENT;
967 invalidate();
968 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800969}