Sunny Goyal | 0fc1be1 | 2014-08-11 17:05:23 -0700 | [diff] [blame] | 1 | /* |
| 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 Goyal | ff57227 | 2014-07-23 13:58:07 -0700 | [diff] [blame] | 17 | package com.android.launcher3; |
| 18 | |
Sunny Goyal | ac36643 | 2015-08-14 17:10:14 -0700 | [diff] [blame] | 19 | import android.annotation.TargetApi; |
Sunny Goyal | ff57227 | 2014-07-23 13:58:07 -0700 | [diff] [blame] | 20 | import android.content.Context; |
Sunny Goyal | 0fc1be1 | 2014-08-11 17:05:23 -0700 | [diff] [blame] | 21 | import android.content.Intent; |
| 22 | import android.content.res.Resources.Theme; |
| 23 | import android.graphics.Bitmap; |
| 24 | import android.graphics.Canvas; |
Sunny Goyal | ac36643 | 2015-08-14 17:10:14 -0700 | [diff] [blame] | 25 | import android.graphics.Color; |
| 26 | import android.graphics.PorterDuff; |
Sunny Goyal | 0fc1be1 | 2014-08-11 17:05:23 -0700 | [diff] [blame] | 27 | import android.graphics.Rect; |
| 28 | import android.graphics.drawable.Drawable; |
Sunny Goyal | ac36643 | 2015-08-14 17:10:14 -0700 | [diff] [blame] | 29 | import android.os.Build; |
Sunny Goyal | ff57227 | 2014-07-23 13:58:07 -0700 | [diff] [blame] | 30 | import android.os.Bundle; |
Sunny Goyal | e7b8cd9 | 2014-08-27 14:04:33 -0700 | [diff] [blame] | 31 | import android.text.Layout; |
| 32 | import android.text.StaticLayout; |
| 33 | import android.text.TextPaint; |
| 34 | import android.util.TypedValue; |
Sunny Goyal | ff57227 | 2014-07-23 13:58:07 -0700 | [diff] [blame] | 35 | import android.view.View; |
| 36 | import android.view.View.OnClickListener; |
Sunny Goyal | ff57227 | 2014-07-23 13:58:07 -0700 | [diff] [blame] | 37 | |
| 38 | public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implements OnClickListener { |
Sunny Goyal | ac36643 | 2015-08-14 17:10:14 -0700 | [diff] [blame] | 39 | private static final float SETUP_ICON_SIZE_FACTOR = 2f / 5; |
| 40 | private static final float MIN_SATUNATION = 0.7f; |
Sunny Goyal | ff57227 | 2014-07-23 13:58:07 -0700 | [diff] [blame] | 41 | |
Sunny Goyal | 0fc1be1 | 2014-08-11 17:05:23 -0700 | [diff] [blame] | 42 | private static Theme sPreloaderTheme; |
Sunny Goyal | ff57227 | 2014-07-23 13:58:07 -0700 | [diff] [blame] | 43 | |
Sunny Goyal | 0fc1be1 | 2014-08-11 17:05:23 -0700 | [diff] [blame] | 44 | private final Rect mRect = new Rect(); |
| 45 | private View mDefaultView; |
Sunny Goyal | ff57227 | 2014-07-23 13:58:07 -0700 | [diff] [blame] | 46 | private OnClickListener mClickListener; |
Sunny Goyal | 0fc1be1 | 2014-08-11 17:05:23 -0700 | [diff] [blame] | 47 | private final LauncherAppWidgetInfo mInfo; |
| 48 | private final int mStartState; |
| 49 | private final Intent mIconLookupIntent; |
Sunny Goyal | 9b4b081 | 2014-10-08 10:47:28 -0700 | [diff] [blame] | 50 | private final boolean mDisabledForSafeMode; |
Adam Cohen | 2e6da15 | 2015-05-06 11:42:25 -0700 | [diff] [blame] | 51 | private Launcher mLauncher; |
Sunny Goyal | ff57227 | 2014-07-23 13:58:07 -0700 | [diff] [blame] | 52 | |
Sunny Goyal | 0fc1be1 | 2014-08-11 17:05:23 -0700 | [diff] [blame] | 53 | private Bitmap mIcon; |
Sunny Goyal | 0fc1be1 | 2014-08-11 17:05:23 -0700 | [diff] [blame] | 54 | |
| 55 | private Drawable mCenterDrawable; |
Sunny Goyal | ac36643 | 2015-08-14 17:10:14 -0700 | [diff] [blame] | 56 | private Drawable mSettingIconDrawable; |
Sunny Goyal | 0fc1be1 | 2014-08-11 17:05:23 -0700 | [diff] [blame] | 57 | |
| 58 | private boolean mDrawableSizeChanged; |
| 59 | |
Sunny Goyal | e7b8cd9 | 2014-08-27 14:04:33 -0700 | [diff] [blame] | 60 | private final TextPaint mPaint; |
| 61 | private Layout mSetupTextLayout; |
| 62 | |
Sunny Goyal | ac36643 | 2015-08-14 17:10:14 -0700 | [diff] [blame] | 63 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) |
Sunny Goyal | 9b4b081 | 2014-10-08 10:47:28 -0700 | [diff] [blame] | 64 | public PendingAppWidgetHostView(Context context, LauncherAppWidgetInfo info, |
| 65 | boolean disabledForSafeMode) { |
Sunny Goyal | ff57227 | 2014-07-23 13:58:07 -0700 | [diff] [blame] | 66 | super(context); |
Adam Cohen | 2e6da15 | 2015-05-06 11:42:25 -0700 | [diff] [blame] | 67 | |
| 68 | mLauncher = (Launcher) context; |
Sunny Goyal | 0fc1be1 | 2014-08-11 17:05:23 -0700 | [diff] [blame] | 69 | mInfo = info; |
| 70 | mStartState = info.restoreStatus; |
| 71 | mIconLookupIntent = new Intent().setComponent(info.providerName); |
Sunny Goyal | 9b4b081 | 2014-10-08 10:47:28 -0700 | [diff] [blame] | 72 | mDisabledForSafeMode = disabledForSafeMode; |
Sunny Goyal | 0fc1be1 | 2014-08-11 17:05:23 -0700 | [diff] [blame] | 73 | |
Sunny Goyal | e7b8cd9 | 2014-08-27 14:04:33 -0700 | [diff] [blame] | 74 | mPaint = new TextPaint(); |
| 75 | mPaint.setColor(0xFFFFFFFF); |
| 76 | mPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, |
Adam Cohen | 2e6da15 | 2015-05-06 11:42:25 -0700 | [diff] [blame] | 77 | mLauncher.getDeviceProfile().iconTextSizePx, getResources().getDisplayMetrics())); |
Sunny Goyal | 0fc1be1 | 2014-08-11 17:05:23 -0700 | [diff] [blame] | 78 | setBackgroundResource(R.drawable.quantum_panel_dark); |
| 79 | setWillNotDraw(false); |
Sunny Goyal | ac36643 | 2015-08-14 17:10:14 -0700 | [diff] [blame] | 80 | |
Sunny Goyal | 9fc953b | 2015-08-17 12:24:25 -0700 | [diff] [blame] | 81 | if (Utilities.ATLEAST_LOLLIPOP) { |
Sunny Goyal | ac36643 | 2015-08-14 17:10:14 -0700 | [diff] [blame] | 82 | setElevation(getResources().getDimension(R.dimen.pending_widget_elevation)); |
| 83 | } |
Sunny Goyal | ff57227 | 2014-07-23 13:58:07 -0700 | [diff] [blame] | 84 | } |
| 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 Goyal | 0fc1be1 | 2014-08-11 17:05:23 -0700 | [diff] [blame] | 95 | mDefaultView = mInflater.inflate(R.layout.appwidget_not_ready, this, false); |
Sunny Goyal | ff57227 | 2014-07-23 13:58:07 -0700 | [diff] [blame] | 96 | 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 Goyal | 0fc1be1 | 2014-08-11 17:05:23 -0700 | [diff] [blame] | 107 | @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 Goyal | 9b4b081 | 2014-10-08 10:47:28 -0700 | [diff] [blame] | 125 | if (mCenterDrawable != null) { |
| 126 | mCenterDrawable.setCallback(null); |
| 127 | mCenterDrawable = null; |
Sunny Goyal | 0fc1be1 | 2014-08-11 17:05:23 -0700 | [diff] [blame] | 128 | } |
| 129 | if (mIcon != null) { |
Sunny Goyal | 9b4b081 | 2014-10-08 10:47:28 -0700 | [diff] [blame] | 130 | // 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 Goyal | 53d7ee4 | 2015-05-22 12:25:45 -0700 | [diff] [blame] | 135 | FastBitmapDrawable disabledIcon = mLauncher.createIconDrawable(mIcon); |
Winson | c088049 | 2015-08-21 11:16:27 -0700 | [diff] [blame] | 136 | disabledIcon.setState(FastBitmapDrawable.State.DISABLED); |
Sunny Goyal | 9b4b081 | 2014-10-08 10:47:28 -0700 | [diff] [blame] | 137 | mCenterDrawable = disabledIcon; |
Sunny Goyal | ac36643 | 2015-08-14 17:10:14 -0700 | [diff] [blame] | 138 | mSettingIconDrawable = null; |
Sunny Goyal | 9b4b081 | 2014-10-08 10:47:28 -0700 | [diff] [blame] | 139 | } else if (isReadyForClickSetup()) { |
Sunny Goyal | ac36643 | 2015-08-14 17:10:14 -0700 | [diff] [blame] | 140 | mCenterDrawable = new FastBitmapDrawable(mIcon); |
| 141 | mSettingIconDrawable = getResources().getDrawable(R.drawable.ic_setting).mutate(); |
| 142 | |
| 143 | updateSettingColor(); |
Sunny Goyal | 0fc1be1 | 2014-08-11 17:05:23 -0700 | [diff] [blame] | 144 | } else { |
| 145 | if (sPreloaderTheme == null) { |
| 146 | sPreloaderTheme = getResources().newTheme(); |
| 147 | sPreloaderTheme.applyStyle(R.style.PreloadIcon, true); |
| 148 | } |
| 149 | |
Sunny Goyal | 53d7ee4 | 2015-05-22 12:25:45 -0700 | [diff] [blame] | 150 | FastBitmapDrawable drawable = mLauncher.createIconDrawable(mIcon); |
Sunny Goyal | 9b4b081 | 2014-10-08 10:47:28 -0700 | [diff] [blame] | 151 | mCenterDrawable = new PreloadIconDrawable(drawable, sPreloaderTheme); |
| 152 | mCenterDrawable.setCallback(this); |
Sunny Goyal | ac36643 | 2015-08-14 17:10:14 -0700 | [diff] [blame] | 153 | mSettingIconDrawable = null; |
Sunny Goyal | 0fc1be1 | 2014-08-11 17:05:23 -0700 | [diff] [blame] | 154 | applyState(); |
| 155 | } |
| 156 | mDrawableSizeChanged = true; |
Sunny Goyal | ff57227 | 2014-07-23 13:58:07 -0700 | [diff] [blame] | 157 | } |
| 158 | } |
| 159 | |
Sunny Goyal | ac36643 | 2015-08-14 17:10:14 -0700 | [diff] [blame] | 160 | 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 Goyal | ff57227 | 2014-07-23 13:58:07 -0700 | [diff] [blame] | 172 | @Override |
Sunny Goyal | 0fc1be1 | 2014-08-11 17:05:23 -0700 | [diff] [blame] | 173 | protected boolean verifyDrawable(Drawable who) { |
Sunny Goyal | 9b4b081 | 2014-10-08 10:47:28 -0700 | [diff] [blame] | 174 | return (who == mCenterDrawable) || super.verifyDrawable(who); |
Sunny Goyal | ff57227 | 2014-07-23 13:58:07 -0700 | [diff] [blame] | 175 | } |
| 176 | |
Sunny Goyal | 0fc1be1 | 2014-08-11 17:05:23 -0700 | [diff] [blame] | 177 | public void applyState() { |
Sunny Goyal | 9b4b081 | 2014-10-08 10:47:28 -0700 | [diff] [blame] | 178 | if (mCenterDrawable != null) { |
| 179 | mCenterDrawable.setLevel(Math.max(mInfo.installProgress, 0)); |
Sunny Goyal | ff57227 | 2014-07-23 13:58:07 -0700 | [diff] [blame] | 180 | } |
| 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 Goyal | d478c83 | 2016-04-01 12:04:16 -0700 | [diff] [blame^] | 192 | /** |
| 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 Goyal | ff57227 | 2014-07-23 13:58:07 -0700 | [diff] [blame] | 201 | public boolean isReadyForClickSetup() { |
Sunny Goyal | d478c83 | 2016-04-01 12:04:16 -0700 | [diff] [blame^] | 202 | 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 Goyal | ff57227 | 2014-07-23 13:58:07 -0700 | [diff] [blame] | 205 | } |
Sunny Goyal | 0fc1be1 | 2014-08-11 17:05:23 -0700 | [diff] [blame] | 206 | |
Sunny Goyal | ac36643 | 2015-08-14 17:10:14 -0700 | [diff] [blame] | 207 | 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 Goyal | a682a5d | 2015-10-01 19:35:03 -0700 | [diff] [blame] | 231 | float iconSize = Math.max(0, Math.min(availableWidth, availableHeight)); |
Sunny Goyal | ac36643 | 2015-08-14 17:10:14 -0700 | [diff] [blame] | 232 | |
| 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 Goyal | 5845bea | 2016-02-23 16:43:23 -0800 | [diff] [blame] | 244 | // Icon top when we do not draw the text |
| 245 | int iconTop = (getHeight() - actualIconSize) / 2; |
| 246 | mSetupTextLayout = null; |
Sunny Goyal | ac36643 | 2015-08-14 17:10:14 -0700 | [diff] [blame] | 247 | |
Sunny Goyal | 5845bea | 2016-02-23 16:43:23 -0800 | [diff] [blame] | 248 | 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 Goyal | ac36643 | 2015-08-14 17:10:14 -0700 | [diff] [blame] | 254 | |
Sunny Goyal | 5845bea | 2016-02-23 16:43:23 -0800 | [diff] [blame] | 255 | // Extra icon size due to the setting icon |
| 256 | float minHeightWithText = textHeight + actualIconSize * settingIconScaleFactor |
| 257 | + grid.iconDrawablePaddingPx; |
Sunny Goyal | ac36643 | 2015-08-14 17:10:14 -0700 | [diff] [blame] | 258 | |
Sunny Goyal | 5845bea | 2016-02-23 16:43:23 -0800 | [diff] [blame] | 259 | 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 Goyal | ac36643 | 2015-08-14 17:10:14 -0700 | [diff] [blame] | 268 | } |
| 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 Goyal | 0fc1be1 | 2014-08-11 17:05:23 -0700 | [diff] [blame] | 288 | @Override |
| 289 | protected void onDraw(Canvas canvas) { |
Sunny Goyal | 9b4b081 | 2014-10-08 10:47:28 -0700 | [diff] [blame] | 290 | if (mCenterDrawable == null) { |
| 291 | // Nothing to draw |
| 292 | return; |
| 293 | } |
| 294 | |
Sunny Goyal | ac36643 | 2015-08-14 17:10:14 -0700 | [diff] [blame] | 295 | if (mDrawableSizeChanged) { |
| 296 | updateDrawableBounds(); |
| 297 | mDrawableSizeChanged = false; |
Sunny Goyal | 0fc1be1 | 2014-08-11 17:05:23 -0700 | [diff] [blame] | 298 | } |
Sunny Goyal | ac36643 | 2015-08-14 17:10:14 -0700 | [diff] [blame] | 299 | |
| 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 Goyal | 0fc1be1 | 2014-08-11 17:05:23 -0700 | [diff] [blame] | 311 | } |
Sunny Goyal | ff57227 | 2014-07-23 13:58:07 -0700 | [diff] [blame] | 312 | } |