1 /* 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 17 package com.android.launcher3; 18 19 import android.animation.ValueAnimator; 20 import android.animation.ValueAnimator.AnimatorUpdateListener; 21 import android.content.res.Resources; 22 import android.graphics.Bitmap; 23 import android.graphics.Canvas; 24 import android.graphics.Paint; 25 import android.graphics.Point; 26 import android.graphics.PorterDuff; 27 import android.graphics.PorterDuffColorFilter; 28 import android.graphics.Rect; 29 import android.view.View; 30 import android.view.animation.DecelerateInterpolator; 31 32 public class DragView extends View { 33 private static float sDragAlpha = 1f; 34 35 private Bitmap mBitmap; 36 private Bitmap mCrossFadeBitmap; 37 private Paint mPaint; 38 private int mRegistrationX; 39 private int mRegistrationY; 40 41 private Point mDragVisualizeOffset = null; 42 private Rect mDragRegion = null; 43 private DragLayer mDragLayer = null; 44 private boolean mHasDrawn = false; 45 private float mCrossFadeProgress = 0f; 46 47 ValueAnimator mAnim; 48 private float mOffsetX = 0.0f; 49 private float mOffsetY = 0.0f; 50 private float mInitialScale = 1f; 51 // The intrinsic icon scale factor is the scale factor for a drag icon over the workspace 52 // size. This is ignored for non-icons. 53 private float mIntrinsicIconScale = 1f; 54 55 /** 56 * Construct the drag view. 57 * <p> 58 * The registration point is the point inside our view that the touch events should 59 * be centered upon. 60 * 61 * @param launcher The Launcher instance 62 * @param bitmap The view that we're dragging around. We scale it up when we draw it. 63 * @param registrationX The x coordinate of the registration point. 64 * @param registrationY The y coordinate of the registration point. 65 */ DragView(Launcher launcher, Bitmap bitmap, int registrationX, int registrationY, int left, int top, int width, int height, final float initialScale)66 public DragView(Launcher launcher, Bitmap bitmap, int registrationX, int registrationY, 67 int left, int top, int width, int height, final float initialScale) { 68 super(launcher); 69 mDragLayer = launcher.getDragLayer(); 70 mInitialScale = initialScale; 71 72 final Resources res = getResources(); 73 final float offsetX = res.getDimensionPixelSize(R.dimen.dragViewOffsetX); 74 final float offsetY = res.getDimensionPixelSize(R.dimen.dragViewOffsetY); 75 final float scaleDps = res.getDimensionPixelSize(R.dimen.dragViewScale); 76 final float scale = (width + scaleDps) / width; 77 78 // Set the initial scale to avoid any jumps 79 setScaleX(initialScale); 80 setScaleY(initialScale); 81 82 // Animate the view into the correct position 83 mAnim = LauncherAnimUtils.ofFloat(this, 0f, 1f); 84 mAnim.setDuration(150); 85 mAnim.addUpdateListener(new AnimatorUpdateListener() { 86 @Override 87 public void onAnimationUpdate(ValueAnimator animation) { 88 final float value = (Float) animation.getAnimatedValue(); 89 90 final int deltaX = (int) ((value * offsetX) - mOffsetX); 91 final int deltaY = (int) ((value * offsetY) - mOffsetY); 92 93 mOffsetX += deltaX; 94 mOffsetY += deltaY; 95 setScaleX(initialScale + (value * (scale - initialScale))); 96 setScaleY(initialScale + (value * (scale - initialScale))); 97 if (sDragAlpha != 1f) { 98 setAlpha(sDragAlpha * value + (1f - value)); 99 } 100 101 if (getParent() == null) { 102 animation.cancel(); 103 } else { 104 setTranslationX(getTranslationX() + deltaX); 105 setTranslationY(getTranslationY() + deltaY); 106 } 107 } 108 }); 109 110 mBitmap = Bitmap.createBitmap(bitmap, left, top, width, height); 111 setDragRegion(new Rect(0, 0, width, height)); 112 113 // The point in our scaled bitmap that the touch events are located 114 mRegistrationX = registrationX; 115 mRegistrationY = registrationY; 116 117 // Force a measure, because Workspace uses getMeasuredHeight() before the layout pass 118 int ms = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); 119 measure(ms, ms); 120 mPaint = new Paint(Paint.FILTER_BITMAP_FLAG); 121 } 122 123 /** Sets the scale of the view over the normal workspace icon size. */ setIntrinsicIconScaleFactor(float scale)124 public void setIntrinsicIconScaleFactor(float scale) { 125 mIntrinsicIconScale = scale; 126 } 127 getIntrinsicIconScaleFactor()128 public float getIntrinsicIconScaleFactor() { 129 return mIntrinsicIconScale; 130 } 131 getOffsetY()132 public float getOffsetY() { 133 return mOffsetY; 134 } 135 getDragRegionLeft()136 public int getDragRegionLeft() { 137 return mDragRegion.left; 138 } 139 getDragRegionTop()140 public int getDragRegionTop() { 141 return mDragRegion.top; 142 } 143 getDragRegionWidth()144 public int getDragRegionWidth() { 145 return mDragRegion.width(); 146 } 147 getDragRegionHeight()148 public int getDragRegionHeight() { 149 return mDragRegion.height(); 150 } 151 setDragVisualizeOffset(Point p)152 public void setDragVisualizeOffset(Point p) { 153 mDragVisualizeOffset = p; 154 } 155 getDragVisualizeOffset()156 public Point getDragVisualizeOffset() { 157 return mDragVisualizeOffset; 158 } 159 setDragRegion(Rect r)160 public void setDragRegion(Rect r) { 161 mDragRegion = r; 162 } 163 getDragRegion()164 public Rect getDragRegion() { 165 return mDragRegion; 166 } 167 getInitialScale()168 public float getInitialScale() { 169 return mInitialScale; 170 } 171 updateInitialScaleToCurrentScale()172 public void updateInitialScaleToCurrentScale() { 173 mInitialScale = getScaleX(); 174 } 175 176 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)177 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 178 setMeasuredDimension(mBitmap.getWidth(), mBitmap.getHeight()); 179 } 180 181 @Override onDraw(Canvas canvas)182 protected void onDraw(Canvas canvas) { 183 @SuppressWarnings("all") // suppress dead code warning 184 final boolean debug = false; 185 if (debug) { 186 Paint p = new Paint(); 187 p.setStyle(Paint.Style.FILL); 188 p.setColor(0x66ffffff); 189 canvas.drawRect(0, 0, getWidth(), getHeight(), p); 190 } 191 192 mHasDrawn = true; 193 boolean crossFade = mCrossFadeProgress > 0 && mCrossFadeBitmap != null; 194 if (crossFade) { 195 int alpha = crossFade ? (int) (255 * (1 - mCrossFadeProgress)) : 255; 196 mPaint.setAlpha(alpha); 197 } 198 canvas.drawBitmap(mBitmap, 0.0f, 0.0f, mPaint); 199 if (crossFade) { 200 mPaint.setAlpha((int) (255 * mCrossFadeProgress)); 201 canvas.save(); 202 float sX = (mBitmap.getWidth() * 1.0f) / mCrossFadeBitmap.getWidth(); 203 float sY = (mBitmap.getHeight() * 1.0f) / mCrossFadeBitmap.getHeight(); 204 canvas.scale(sX, sY); 205 canvas.drawBitmap(mCrossFadeBitmap, 0.0f, 0.0f, mPaint); 206 canvas.restore(); 207 } 208 } 209 setCrossFadeBitmap(Bitmap crossFadeBitmap)210 public void setCrossFadeBitmap(Bitmap crossFadeBitmap) { 211 mCrossFadeBitmap = crossFadeBitmap; 212 } 213 crossFade(int duration)214 public void crossFade(int duration) { 215 ValueAnimator va = LauncherAnimUtils.ofFloat(this, 0f, 1f); 216 va.setDuration(duration); 217 va.setInterpolator(new DecelerateInterpolator(1.5f)); 218 va.addUpdateListener(new AnimatorUpdateListener() { 219 @Override 220 public void onAnimationUpdate(ValueAnimator animation) { 221 mCrossFadeProgress = animation.getAnimatedFraction(); 222 } 223 }); 224 va.start(); 225 } 226 setColor(int color)227 public void setColor(int color) { 228 if (mPaint == null) { 229 mPaint = new Paint(Paint.FILTER_BITMAP_FLAG); 230 } 231 if (color != 0) { 232 mPaint.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP)); 233 } else { 234 mPaint.setColorFilter(null); 235 } 236 invalidate(); 237 } 238 hasDrawn()239 public boolean hasDrawn() { 240 return mHasDrawn; 241 } 242 243 @Override setAlpha(float alpha)244 public void setAlpha(float alpha) { 245 super.setAlpha(alpha); 246 mPaint.setAlpha((int) (255 * alpha)); 247 invalidate(); 248 } 249 250 /** 251 * Create a window containing this view and show it. 252 * 253 * @param windowToken obtained from v.getWindowToken() from one of your views 254 * @param touchX the x coordinate the user touched in DragLayer coordinates 255 * @param touchY the y coordinate the user touched in DragLayer coordinates 256 */ show(int touchX, int touchY)257 public void show(int touchX, int touchY) { 258 mDragLayer.addView(this); 259 260 // Start the pick-up animation 261 DragLayer.LayoutParams lp = new DragLayer.LayoutParams(0, 0); 262 lp.width = mBitmap.getWidth(); 263 lp.height = mBitmap.getHeight(); 264 lp.customPosition = true; 265 setLayoutParams(lp); 266 setTranslationX(touchX - mRegistrationX); 267 setTranslationY(touchY - mRegistrationY); 268 // Post the animation to skip other expensive work happening on the first frame 269 post(new Runnable() { 270 public void run() { 271 mAnim.start(); 272 } 273 }); 274 } 275 cancelAnimation()276 public void cancelAnimation() { 277 if (mAnim != null && mAnim.isRunning()) { 278 mAnim.cancel(); 279 } 280 } 281 resetLayoutParams()282 public void resetLayoutParams() { 283 mOffsetX = mOffsetY = 0; 284 requestLayout(); 285 } 286 287 /** 288 * Move the window containing this view. 289 * 290 * @param touchX the x coordinate the user touched in DragLayer coordinates 291 * @param touchY the y coordinate the user touched in DragLayer coordinates 292 */ move(int touchX, int touchY)293 void move(int touchX, int touchY) { 294 setTranslationX(touchX - mRegistrationX + (int) mOffsetX); 295 setTranslationY(touchY - mRegistrationY + (int) mOffsetY); 296 } 297 remove()298 void remove() { 299 if (getParent() != null) { 300 mDragLayer.removeView(DragView.this); 301 } 302 } 303 } 304 305