blob: a455026fa6aa7279179ae3ed443f94cab874be53 [file] [log] [blame]
Sunny Goyal0fc1be12014-08-11 17:05:23 -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
Sunny Goyalff572272014-07-23 13:58:07 -070017package com.android.launcher3;
18
Sunny Goyalac366432015-08-14 17:10:14 -070019import android.annotation.TargetApi;
Sunny Goyalff572272014-07-23 13:58:07 -070020import android.content.Context;
Sunny Goyal0fc1be12014-08-11 17:05:23 -070021import android.content.Intent;
22import android.content.res.Resources.Theme;
23import android.graphics.Bitmap;
24import android.graphics.Canvas;
Sunny Goyalac366432015-08-14 17:10:14 -070025import android.graphics.Color;
26import android.graphics.PorterDuff;
Sunny Goyal0fc1be12014-08-11 17:05:23 -070027import android.graphics.Rect;
28import android.graphics.drawable.Drawable;
Sunny Goyalac366432015-08-14 17:10:14 -070029import android.os.Build;
Sunny Goyalff572272014-07-23 13:58:07 -070030import android.os.Bundle;
Sunny Goyale7b8cd92014-08-27 14:04:33 -070031import android.text.Layout;
32import android.text.StaticLayout;
33import android.text.TextPaint;
34import android.util.TypedValue;
Sunny Goyalff572272014-07-23 13:58:07 -070035import android.view.View;
36import android.view.View.OnClickListener;
Sunny Goyalff572272014-07-23 13:58:07 -070037
38public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implements OnClickListener {
Sunny Goyalac366432015-08-14 17:10:14 -070039 private static final float SETUP_ICON_SIZE_FACTOR = 2f / 5;
40 private static final float MIN_SATUNATION = 0.7f;
Sunny Goyalff572272014-07-23 13:58:07 -070041
Sunny Goyal0fc1be12014-08-11 17:05:23 -070042 private static Theme sPreloaderTheme;
Sunny Goyalff572272014-07-23 13:58:07 -070043
Sunny Goyal0fc1be12014-08-11 17:05:23 -070044 private final Rect mRect = new Rect();
45 private View mDefaultView;
Sunny Goyalff572272014-07-23 13:58:07 -070046 private OnClickListener mClickListener;
Sunny Goyal0fc1be12014-08-11 17:05:23 -070047 private final LauncherAppWidgetInfo mInfo;
48 private final int mStartState;
49 private final Intent mIconLookupIntent;
Sunny Goyal9b4b0812014-10-08 10:47:28 -070050 private final boolean mDisabledForSafeMode;
Adam Cohen2e6da152015-05-06 11:42:25 -070051 private Launcher mLauncher;
Sunny Goyalff572272014-07-23 13:58:07 -070052
Sunny Goyal0fc1be12014-08-11 17:05:23 -070053 private Bitmap mIcon;
Sunny Goyal0fc1be12014-08-11 17:05:23 -070054
55 private Drawable mCenterDrawable;
Sunny Goyalac366432015-08-14 17:10:14 -070056 private Drawable mSettingIconDrawable;
Sunny Goyal0fc1be12014-08-11 17:05:23 -070057
58 private boolean mDrawableSizeChanged;
59
Sunny Goyale7b8cd92014-08-27 14:04:33 -070060 private final TextPaint mPaint;
61 private Layout mSetupTextLayout;
62
Sunny Goyalac366432015-08-14 17:10:14 -070063 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
Sunny Goyal9b4b0812014-10-08 10:47:28 -070064 public PendingAppWidgetHostView(Context context, LauncherAppWidgetInfo info,
65 boolean disabledForSafeMode) {
Sunny Goyalff572272014-07-23 13:58:07 -070066 super(context);
Adam Cohen2e6da152015-05-06 11:42:25 -070067
68 mLauncher = (Launcher) context;
Sunny Goyal0fc1be12014-08-11 17:05:23 -070069 mInfo = info;
70 mStartState = info.restoreStatus;
71 mIconLookupIntent = new Intent().setComponent(info.providerName);
Sunny Goyal9b4b0812014-10-08 10:47:28 -070072 mDisabledForSafeMode = disabledForSafeMode;
Sunny Goyal0fc1be12014-08-11 17:05:23 -070073
Sunny Goyale7b8cd92014-08-27 14:04:33 -070074 mPaint = new TextPaint();
75 mPaint.setColor(0xFFFFFFFF);
76 mPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX,
Adam Cohen2e6da152015-05-06 11:42:25 -070077 mLauncher.getDeviceProfile().iconTextSizePx, getResources().getDisplayMetrics()));
Sunny Goyal0fc1be12014-08-11 17:05:23 -070078 setBackgroundResource(R.drawable.quantum_panel_dark);
79 setWillNotDraw(false);
Sunny Goyalac366432015-08-14 17:10:14 -070080
Sunny Goyal9fc953b2015-08-17 12:24:25 -070081 if (Utilities.ATLEAST_LOLLIPOP) {
Sunny Goyalac366432015-08-14 17:10:14 -070082 setElevation(getResources().getDimension(R.dimen.pending_widget_elevation));
83 }
Sunny Goyalff572272014-07-23 13:58:07 -070084 }
85
86 @Override
87 public void updateAppWidgetSize(Bundle newOptions, int minWidth, int minHeight, int maxWidth,
88 int maxHeight) {
89 // No-op
90 }
91
92 @Override
93 protected View getDefaultView() {
94 if (mDefaultView == null) {
Sunny Goyal0fc1be12014-08-11 17:05:23 -070095 mDefaultView = mInflater.inflate(R.layout.appwidget_not_ready, this, false);
Sunny Goyalff572272014-07-23 13:58:07 -070096 mDefaultView.setOnClickListener(this);
97 applyState();
98 }
99 return mDefaultView;
100 }
101
102 @Override
103 public void setOnClickListener(OnClickListener l) {
104 mClickListener = l;
105 }
106
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700107 @Override
108 public boolean isReinflateRequired() {
109 // Re inflate is required any time the widget restore status changes
110 return mStartState != mInfo.restoreStatus;
111 }
112
113 @Override
114 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
115 super.onSizeChanged(w, h, oldw, oldh);
116 mDrawableSizeChanged = true;
117 }
118
119 public void updateIcon(IconCache cache) {
120 Bitmap icon = cache.getIcon(mIconLookupIntent, mInfo.user);
121 if (mIcon == icon) {
122 return;
123 }
124 mIcon = icon;
Sunny Goyal9b4b0812014-10-08 10:47:28 -0700125 if (mCenterDrawable != null) {
126 mCenterDrawable.setCallback(null);
127 mCenterDrawable = null;
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700128 }
129 if (mIcon != null) {
Sunny Goyal9b4b0812014-10-08 10:47:28 -0700130 // The view displays three modes,
131 // 1) App icon in the center
132 // 2) Preload icon in the center
133 // 3) Setup icon in the center and app icon in the top right corner.
134 if (mDisabledForSafeMode) {
Sunny Goyal53d7ee42015-05-22 12:25:45 -0700135 FastBitmapDrawable disabledIcon = mLauncher.createIconDrawable(mIcon);
Winsonc0880492015-08-21 11:16:27 -0700136 disabledIcon.setState(FastBitmapDrawable.State.DISABLED);
Sunny Goyal9b4b0812014-10-08 10:47:28 -0700137 mCenterDrawable = disabledIcon;
Sunny Goyalac366432015-08-14 17:10:14 -0700138 mSettingIconDrawable = null;
Sunny Goyal9b4b0812014-10-08 10:47:28 -0700139 } else if (isReadyForClickSetup()) {
Sunny Goyalac366432015-08-14 17:10:14 -0700140 mCenterDrawable = new FastBitmapDrawable(mIcon);
141 mSettingIconDrawable = getResources().getDrawable(R.drawable.ic_setting).mutate();
142
143 updateSettingColor();
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700144 } else {
145 if (sPreloaderTheme == null) {
146 sPreloaderTheme = getResources().newTheme();
147 sPreloaderTheme.applyStyle(R.style.PreloadIcon, true);
148 }
149
Sunny Goyal53d7ee42015-05-22 12:25:45 -0700150 FastBitmapDrawable drawable = mLauncher.createIconDrawable(mIcon);
Sunny Goyal9b4b0812014-10-08 10:47:28 -0700151 mCenterDrawable = new PreloadIconDrawable(drawable, sPreloaderTheme);
152 mCenterDrawable.setCallback(this);
Sunny Goyalac366432015-08-14 17:10:14 -0700153 mSettingIconDrawable = null;
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700154 applyState();
155 }
156 mDrawableSizeChanged = true;
Sunny Goyalff572272014-07-23 13:58:07 -0700157 }
158 }
159
Sunny Goyalac366432015-08-14 17:10:14 -0700160 private void updateSettingColor() {
161 int color = Utilities.findDominantColorByHue(mIcon, 20);
162 // Make the dominant color bright.
163 float[] hsv = new float[3];
164 Color.colorToHSV(color, hsv);
165 hsv[1] = Math.min(hsv[1], MIN_SATUNATION);
166 hsv[2] = 1;
167 color = Color.HSVToColor(hsv);
168
169 mSettingIconDrawable.setColorFilter(color, PorterDuff.Mode.SRC_IN);
170 }
171
Sunny Goyalff572272014-07-23 13:58:07 -0700172 @Override
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700173 protected boolean verifyDrawable(Drawable who) {
Sunny Goyal9b4b0812014-10-08 10:47:28 -0700174 return (who == mCenterDrawable) || super.verifyDrawable(who);
Sunny Goyalff572272014-07-23 13:58:07 -0700175 }
176
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700177 public void applyState() {
Sunny Goyal9b4b0812014-10-08 10:47:28 -0700178 if (mCenterDrawable != null) {
179 mCenterDrawable.setLevel(Math.max(mInfo.installProgress, 0));
Sunny Goyalff572272014-07-23 13:58:07 -0700180 }
181 }
182
183 @Override
184 public void onClick(View v) {
185 // AppWidgetHostView blocks all click events on the root view. Instead handle click events
186 // on the content and pass it along.
187 if (mClickListener != null) {
188 mClickListener.onClick(this);
189 }
190 }
191
Sunny Goyald478c832016-04-01 12:04:16 -0700192 /**
193 * A pending widget is ready for setup after the provider is installed and
194 * 1) Widget id is not valid: the widget id is not yet bound to the provider, probably
195 * because the launcher doesn't have appropriate permissions.
196 * Note that we would still have an allocated id as that does not
197 * require any permissions and can be done during view inflation.
198 * 2) UI is not ready: the id is valid and the bound. But the widget has a configure activity
199 * which needs to be called once.
200 */
Sunny Goyalff572272014-07-23 13:58:07 -0700201 public boolean isReadyForClickSetup() {
Sunny Goyald478c832016-04-01 12:04:16 -0700202 return !mInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
203 && (mInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY)
204 || mInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID));
Sunny Goyalff572272014-07-23 13:58:07 -0700205 }
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700206
Sunny Goyalac366432015-08-14 17:10:14 -0700207 private void updateDrawableBounds() {
208 DeviceProfile grid = mLauncher.getDeviceProfile();
209 int paddingTop = getPaddingTop();
210 int paddingBottom = getPaddingBottom();
211 int paddingLeft = getPaddingLeft();
212 int paddingRight = getPaddingRight();
213
214 int minPadding = getResources()
215 .getDimensionPixelSize(R.dimen.pending_widget_min_padding);
216
217 int availableWidth = getWidth() - paddingLeft - paddingRight - 2 * minPadding;
218 int availableHeight = getHeight() - paddingTop - paddingBottom - 2 * minPadding;
219
220 if (mSettingIconDrawable == null) {
221 int outset = (mCenterDrawable instanceof PreloadIconDrawable) ?
222 ((PreloadIconDrawable) mCenterDrawable).getOutset() : 0;
223 int maxSize = grid.iconSizePx + 2 * outset;
224 int size = Math.min(maxSize, Math.min(availableWidth, availableHeight));
225
226 mRect.set(0, 0, size, size);
227 mRect.inset(outset, outset);
228 mRect.offsetTo((getWidth() - mRect.width()) / 2, (getHeight() - mRect.height()) / 2);
229 mCenterDrawable.setBounds(mRect);
230 } else {
Sunny Goyala682a5d2015-10-01 19:35:03 -0700231 float iconSize = Math.max(0, Math.min(availableWidth, availableHeight));
Sunny Goyalac366432015-08-14 17:10:14 -0700232
233 // Use twice the setting size factor, as the setting is drawn at a corner and the
234 // icon is drawn in the center.
235 float settingIconScaleFactor = 1 + SETUP_ICON_SIZE_FACTOR * 2;
236 int maxSize = Math.max(availableWidth, availableHeight);
237 if (iconSize * settingIconScaleFactor > maxSize) {
238 // There is an overlap
239 iconSize = maxSize / settingIconScaleFactor;
240 }
241
242 int actualIconSize = (int) Math.min(iconSize, grid.iconSizePx);
243
Sunny Goyal5845bea2016-02-23 16:43:23 -0800244 // Icon top when we do not draw the text
245 int iconTop = (getHeight() - actualIconSize) / 2;
246 mSetupTextLayout = null;
Sunny Goyalac366432015-08-14 17:10:14 -0700247
Sunny Goyal5845bea2016-02-23 16:43:23 -0800248 if (availableWidth > 0) {
249 // Recreate the setup text.
250 mSetupTextLayout = new StaticLayout(
251 getResources().getText(R.string.gadget_setup_text), mPaint, availableWidth,
252 Layout.Alignment.ALIGN_CENTER, 1, 0, true);
253 int textHeight = mSetupTextLayout.getHeight();
Sunny Goyalac366432015-08-14 17:10:14 -0700254
Sunny Goyal5845bea2016-02-23 16:43:23 -0800255 // Extra icon size due to the setting icon
256 float minHeightWithText = textHeight + actualIconSize * settingIconScaleFactor
257 + grid.iconDrawablePaddingPx;
Sunny Goyalac366432015-08-14 17:10:14 -0700258
Sunny Goyal5845bea2016-02-23 16:43:23 -0800259 if (minHeightWithText < availableHeight) {
260 // We can draw the text as well
261 iconTop = (getHeight() - textHeight -
262 grid.iconDrawablePaddingPx - actualIconSize) / 2;
263
264 } else {
265 // We can't draw the text. Let the iconTop be same as before.
266 mSetupTextLayout = null;
267 }
Sunny Goyalac366432015-08-14 17:10:14 -0700268 }
269
270 mRect.set(0, 0, actualIconSize, actualIconSize);
271 mRect.offset((getWidth() - actualIconSize) / 2, iconTop);
272 mCenterDrawable.setBounds(mRect);
273
274 mRect.left = paddingLeft + minPadding;
275 mRect.right = mRect.left + (int) (SETUP_ICON_SIZE_FACTOR * actualIconSize);
276 mRect.top = paddingTop + minPadding;
277 mRect.bottom = mRect.top + (int) (SETUP_ICON_SIZE_FACTOR * actualIconSize);
278 mSettingIconDrawable.setBounds(mRect);
279
280 if (mSetupTextLayout != null) {
281 // Set up position for dragging the text
282 mRect.left = paddingLeft + minPadding;
283 mRect.top = mCenterDrawable.getBounds().bottom + grid.iconDrawablePaddingPx;
284 }
285 }
286 }
287
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700288 @Override
289 protected void onDraw(Canvas canvas) {
Sunny Goyal9b4b0812014-10-08 10:47:28 -0700290 if (mCenterDrawable == null) {
291 // Nothing to draw
292 return;
293 }
294
Sunny Goyalac366432015-08-14 17:10:14 -0700295 if (mDrawableSizeChanged) {
296 updateDrawableBounds();
297 mDrawableSizeChanged = false;
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700298 }
Sunny Goyalac366432015-08-14 17:10:14 -0700299
300 mCenterDrawable.draw(canvas);
301 if (mSettingIconDrawable != null) {
302 mSettingIconDrawable.draw(canvas);
303 }
304 if (mSetupTextLayout != null) {
305 canvas.save();
306 canvas.translate(mRect.left, mRect.top);
307 mSetupTextLayout.draw(canvas);
308 canvas.restore();
309 }
310
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700311 }
Sunny Goyalff572272014-07-23 13:58:07 -0700312}