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
18package com.android.launcher2;
19
20import android.animation.ValueAnimator;
21import android.animation.ValueAnimator.AnimatorUpdateListener;
22import android.content.res.Resources;
23import android.graphics.Bitmap;
24import android.graphics.Canvas;
25import android.graphics.Paint;
26import android.graphics.Point;
27import android.graphics.PorterDuff;
28import android.graphics.PorterDuffColorFilter;
29import android.graphics.Rect;
30import android.view.View;
31import android.view.animation.DecelerateInterpolator;
32
33import com.android.launcher.R;
34
35public class DragView extends View {
36    private static float sDragAlpha = 1f;
37
38    private Bitmap mBitmap;
39    private Bitmap mCrossFadeBitmap;
40    private Paint mPaint;
41    private int mRegistrationX;
42    private int mRegistrationY;
43
44    private Point mDragVisualizeOffset = null;
45    private Rect mDragRegion = null;
46    private DragLayer mDragLayer = null;
47    private boolean mHasDrawn = false;
48    private float mCrossFadeProgress = 0f;
49
50    ValueAnimator mAnim;
51    private float mOffsetX = 0.0f;
52    private float mOffsetY = 0.0f;
53    private float mInitialScale = 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     */
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    public float getOffsetY() {
124        return mOffsetY;
125    }
126
127    public int getDragRegionLeft() {
128        return mDragRegion.left;
129    }
130
131    public int getDragRegionTop() {
132        return mDragRegion.top;
133    }
134
135    public int getDragRegionWidth() {
136        return mDragRegion.width();
137    }
138
139    public int getDragRegionHeight() {
140        return mDragRegion.height();
141    }
142
143    public void setDragVisualizeOffset(Point p) {
144        mDragVisualizeOffset = p;
145    }
146
147    public Point getDragVisualizeOffset() {
148        return mDragVisualizeOffset;
149    }
150
151    public void setDragRegion(Rect r) {
152        mDragRegion = r;
153    }
154
155    public Rect getDragRegion() {
156        return mDragRegion;
157    }
158
159    public float getInitialScale() {
160        return mInitialScale;
161    }
162
163    public void updateInitialScaleToCurrentScale() {
164        mInitialScale = getScaleX();
165    }
166
167    @Override
168    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
169        setMeasuredDimension(mBitmap.getWidth(), mBitmap.getHeight());
170    }
171
172    @Override
173    protected void onDraw(Canvas canvas) {
174        @SuppressWarnings("all") // suppress dead code warning
175        final boolean debug = false;
176        if (debug) {
177            Paint p = new Paint();
178            p.setStyle(Paint.Style.FILL);
179            p.setColor(0x66ffffff);
180            canvas.drawRect(0, 0, getWidth(), getHeight(), p);
181        }
182
183        mHasDrawn = true;
184        boolean crossFade = mCrossFadeProgress > 0 && mCrossFadeBitmap != null;
185        if (crossFade) {
186            int alpha = crossFade ? (int) (255 * (1 - mCrossFadeProgress)) : 255;
187            mPaint.setAlpha(alpha);
188        }
189        canvas.drawBitmap(mBitmap, 0.0f, 0.0f, mPaint);
190        if (crossFade) {
191            mPaint.setAlpha((int) (255 * mCrossFadeProgress));
192            canvas.save();
193            float sX = (mBitmap.getWidth() * 1.0f) / mCrossFadeBitmap.getWidth();
194            float sY = (mBitmap.getHeight() * 1.0f) / mCrossFadeBitmap.getHeight();
195            canvas.scale(sX, sY);
196            canvas.drawBitmap(mCrossFadeBitmap, 0.0f, 0.0f, mPaint);
197            canvas.restore();
198        }
199    }
200
201    public void setCrossFadeBitmap(Bitmap crossFadeBitmap) {
202        mCrossFadeBitmap = crossFadeBitmap;
203    }
204
205    public void crossFade(int duration) {
206        ValueAnimator va = LauncherAnimUtils.ofFloat(this, 0f, 1f);
207        va.setDuration(duration);
208        va.setInterpolator(new DecelerateInterpolator(1.5f));
209        va.addUpdateListener(new AnimatorUpdateListener() {
210            @Override
211            public void onAnimationUpdate(ValueAnimator animation) {
212                mCrossFadeProgress = animation.getAnimatedFraction();
213            }
214        });
215        va.start();
216    }
217
218    public void setColor(int color) {
219        if (mPaint == null) {
220            mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
221        }
222        if (color != 0) {
223            mPaint.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP));
224        } else {
225            mPaint.setColorFilter(null);
226        }
227        invalidate();
228    }
229
230    public boolean hasDrawn() {
231        return mHasDrawn;
232    }
233
234    @Override
235    public void setAlpha(float alpha) {
236        super.setAlpha(alpha);
237        mPaint.setAlpha((int) (255 * alpha));
238        invalidate();
239    }
240
241    /**
242     * Create a window containing this view and show it.
243     *
244     * @param windowToken obtained from v.getWindowToken() from one of your views
245     * @param touchX the x coordinate the user touched in DragLayer coordinates
246     * @param touchY the y coordinate the user touched in DragLayer coordinates
247     */
248    public void show(int touchX, int touchY) {
249        mDragLayer.addView(this);
250
251        // Start the pick-up animation
252        DragLayer.LayoutParams lp = new DragLayer.LayoutParams(0, 0);
253        lp.width = mBitmap.getWidth();
254        lp.height = mBitmap.getHeight();
255        lp.customPosition = true;
256        setLayoutParams(lp);
257        setTranslationX(touchX - mRegistrationX);
258        setTranslationY(touchY - mRegistrationY);
259        // Post the animation to skip other expensive work happening on the first frame
260        post(new Runnable() {
261                public void run() {
262                    mAnim.start();
263                }
264            });
265    }
266
267    public void cancelAnimation() {
268        if (mAnim != null && mAnim.isRunning()) {
269            mAnim.cancel();
270        }
271    }
272
273    public void resetLayoutParams() {
274        mOffsetX = mOffsetY = 0;
275        requestLayout();
276    }
277
278    /**
279     * Move the window containing this view.
280     *
281     * @param touchX the x coordinate the user touched in DragLayer coordinates
282     * @param touchY the y coordinate the user touched in DragLayer coordinates
283     */
284    void move(int touchX, int touchY) {
285        setTranslationX(touchX - mRegistrationX + (int) mOffsetX);
286        setTranslationY(touchY - mRegistrationY + (int) mOffsetY);
287    }
288
289    void remove() {
290        if (getParent() != null) {
291            mDragLayer.removeView(DragView.this);
292        }
293    }
294}
295
296