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 17 package com.android.launcher3; 18 19 import android.content.Context; 20 import android.content.Intent; 21 import android.content.res.Resources.Theme; 22 import android.graphics.Bitmap; 23 import android.graphics.Canvas; 24 import android.graphics.Rect; 25 import android.graphics.drawable.Drawable; 26 import android.os.Bundle; 27 import android.text.Layout; 28 import android.text.StaticLayout; 29 import android.text.TextPaint; 30 import android.util.TypedValue; 31 import android.view.View; 32 import android.view.View.OnClickListener; 33 34 public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implements OnClickListener { 35 36 private static Theme sPreloaderTheme; 37 38 private final Rect mRect = new Rect(); 39 private View mDefaultView; 40 private OnClickListener mClickListener; 41 private final LauncherAppWidgetInfo mInfo; 42 private final int mStartState; 43 private final Intent mIconLookupIntent; 44 45 private Bitmap mIcon; 46 private PreloadIconDrawable mDrawable; 47 48 private Drawable mCenterDrawable; 49 private Drawable mTopCornerDrawable; 50 51 private boolean mDrawableSizeChanged; 52 53 private final TextPaint mPaint; 54 private Layout mSetupTextLayout; 55 56 public PendingAppWidgetHostView(Context context, LauncherAppWidgetInfo info) { 57 super(context); 58 mInfo = info; 59 mStartState = info.restoreStatus; 60 mIconLookupIntent = new Intent().setComponent(info.providerName); 61 62 mPaint = new TextPaint(); 63 mPaint.setColor(0xFFFFFFFF); 64 mPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, 65 getDeviceProfile().iconTextSizePx, getResources().getDisplayMetrics())); 66 setBackgroundResource(R.drawable.quantum_panel_dark); 67 setWillNotDraw(false); 68 } 69 70 @Override 71 public void updateAppWidgetSize(Bundle newOptions, int minWidth, int minHeight, int maxWidth, 72 int maxHeight) { 73 // No-op 74 } 75 76 @Override 77 protected View getDefaultView() { 78 if (mDefaultView == null) { 79 mDefaultView = mInflater.inflate(R.layout.appwidget_not_ready, this, false); 80 mDefaultView.setOnClickListener(this); 81 applyState(); 82 } 83 return mDefaultView; 84 } 85 86 @Override 87 public void setOnClickListener(OnClickListener l) { 88 mClickListener = l; 89 } 90 91 @Override 92 public boolean isReinflateRequired() { 93 // Re inflate is required any time the widget restore status changes 94 return mStartState != mInfo.restoreStatus; 95 } 96 97 @Override 98 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 99 super.onSizeChanged(w, h, oldw, oldh); 100 mDrawableSizeChanged = true; 101 } 102 103 public void updateIcon(IconCache cache) { 104 Bitmap icon = cache.getIcon(mIconLookupIntent, mInfo.user); 105 if (mIcon == icon) { 106 return; 107 } 108 mIcon = icon; 109 if (mDrawable != null) { 110 mDrawable.setCallback(null); 111 mDrawable = null; 112 } 113 if (mIcon != null) { 114 // The view displays two modes, one with a setup icon and another with a preload icon 115 // in the center. 116 if (isReadyForClickSetup()) { 117 mCenterDrawable = getResources().getDrawable(R.drawable.ic_setting); 118 mTopCornerDrawable = new FastBitmapDrawable(mIcon); 119 } else { 120 if (sPreloaderTheme == null) { 121 sPreloaderTheme = getResources().newTheme(); 122 sPreloaderTheme.applyStyle(R.style.PreloadIcon, true); 123 } 124 125 FastBitmapDrawable drawable = Utilities.createIconDrawable(mIcon); 126 mDrawable = new PreloadIconDrawable(drawable, sPreloaderTheme); 127 mDrawable.setCallback(this); 128 applyState(); 129 } 130 mDrawableSizeChanged = true; 131 } 132 } 133 134 @Override 135 protected boolean verifyDrawable(Drawable who) { 136 return (who == mDrawable) || super.verifyDrawable(who); 137 } 138 139 public void applyState() { 140 if (mDrawable != null) { 141 mDrawable.setLevel(Math.max(mInfo.installProgress, 0)); 142 } 143 } 144 145 @Override 146 public void onClick(View v) { 147 // AppWidgetHostView blocks all click events on the root view. Instead handle click events 148 // on the content and pass it along. 149 if (mClickListener != null) { 150 mClickListener.onClick(this); 151 } 152 } 153 154 public boolean isReadyForClickSetup() { 155 return (mInfo.restoreStatus & LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0 156 && (mInfo.restoreStatus & LauncherAppWidgetInfo.FLAG_UI_NOT_READY) != 0; 157 } 158 159 @Override 160 protected void onDraw(Canvas canvas) { 161 if (mDrawable != null) { 162 if (mDrawableSizeChanged) { 163 int maxSize = LauncherAppState.getInstance().getDynamicGrid() 164 .getDeviceProfile().iconSizePx + 2 * mDrawable.getOutset(); 165 int size = Math.min(maxSize, Math.min( 166 getWidth() - getPaddingLeft() - getPaddingRight(), 167 getHeight() - getPaddingTop() - getPaddingBottom())); 168 169 mRect.set(0, 0, size, size); 170 mRect.inset(mDrawable.getOutset(), mDrawable.getOutset()); 171 mRect.offsetTo((getWidth() - mRect.width()) / 2, (getHeight() - mRect.height()) / 2); 172 mDrawable.setBounds(mRect); 173 mDrawableSizeChanged = false; 174 } 175 176 mDrawable.draw(canvas); 177 } else if ((mCenterDrawable != null) && (mTopCornerDrawable != null)) { 178 if (mDrawableSizeChanged) { 179 DeviceProfile grid = getDeviceProfile(); 180 int iconSize = grid.iconSizePx; 181 int paddingTop = getPaddingTop(); 182 int paddingBottom = getPaddingBottom(); 183 int paddingLeft = getPaddingLeft(); 184 int paddingRight = getPaddingRight(); 185 186 int availableWidth = getWidth() - paddingLeft - paddingRight; 187 int availableHeight = getHeight() - paddingTop - paddingBottom; 188 189 // Recreate the setup text. 190 mSetupTextLayout = new StaticLayout( 191 getResources().getText(R.string.gadget_setup_text), mPaint, availableWidth, 192 Layout.Alignment.ALIGN_CENTER, 1, 0, true); 193 if (mSetupTextLayout.getLineCount() == 1) { 194 // The text fits in a single line. No need to draw the setup icon. 195 int size = Math.min(iconSize, Math.min(availableWidth, 196 availableHeight - mSetupTextLayout.getHeight())); 197 mRect.set(0, 0, size, size); 198 mRect.offsetTo((getWidth() - mRect.width()) / 2, 199 (getHeight() - mRect.height() - mSetupTextLayout.getHeight() 200 - grid.iconDrawablePaddingPx) / 2); 201 202 mTopCornerDrawable.setBounds(mRect); 203 204 // Update left and top to indicate the position where the text will be drawn. 205 mRect.left = paddingLeft; 206 mRect.top = mRect.bottom + grid.iconDrawablePaddingPx; 207 } else { 208 // The text can't be drawn in a single line. Draw a setup icon instead. 209 mSetupTextLayout = null; 210 int size = Math.min(iconSize, Math.min( 211 getWidth() - paddingLeft - paddingRight, 212 getHeight() - paddingTop - paddingBottom)); 213 mRect.set(0, 0, size, size); 214 mRect.offsetTo((getWidth() - mRect.width()) / 2, (getHeight() - mRect.height()) / 2); 215 mCenterDrawable.setBounds(mRect); 216 217 size = Math.min(size / 2, 218 Math.max(mRect.top - paddingTop, mRect.left - paddingLeft)); 219 mTopCornerDrawable.setBounds(paddingLeft, paddingTop, 220 paddingLeft + size, paddingTop + size); 221 } 222 mDrawableSizeChanged = false; 223 } 224 225 if (mSetupTextLayout == null) { 226 mCenterDrawable.draw(canvas); 227 mTopCornerDrawable.draw(canvas); 228 } else { 229 canvas.save(); 230 canvas.translate(mRect.left, mRect.top); 231 mSetupTextLayout.draw(canvas); 232 canvas.restore(); 233 mTopCornerDrawable.draw(canvas); 234 } 235 } 236 } 237 238 private DeviceProfile getDeviceProfile() { 239 return LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile(); 240 } 241 } 242